summaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 08:06:26 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 08:06:26 +0000
commit1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5 (patch)
tree6cf8220b628ebd2ccfc1375dd6516c6996e9abcc /tests
parentInitial commit. (diff)
downloadcryptsetup-1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5.tar.xz
cryptsetup-1660d4b7a65d9ad2ce0deaa19d35579ca4084ac5.zip
Adding upstream version 2:2.6.1.upstream/2%2.6.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'tests')
-rwxr-xr-xtests/00modules-test49
-rw-r--r--tests/Makefile.am206
-rw-r--r--tests/Makefile.localtest66
-rwxr-xr-xtests/align-test341
-rwxr-xr-xtests/align-test2449
-rw-r--r--tests/all-symbols-test.c177
-rw-r--r--tests/api-test-2.c5139
-rw-r--r--tests/api-test.c2411
-rw-r--r--tests/api_test.h165
-rwxr-xr-xtests/bitlk-compat-test200
-rw-r--r--tests/bitlk-images.tar.xzbin0 -> 293068 bytes
-rw-r--r--tests/blkid-luks2-pv.img.xzbin0 -> 5236 bytes
-rwxr-xr-xtests/blockwise-compat-test379
-rwxr-xr-xtests/compat-args-test299
-rwxr-xr-xtests/compat-test1137
-rwxr-xr-xtests/compat-test21204
-rw-r--r--tests/compatimage.img.xzbin0 -> 66716 bytes
-rw-r--r--tests/compatimage2.img.xzbin0 -> 66356 bytes
-rw-r--r--tests/compatv10image.img.xzbin0 -> 66688 bytes
-rw-r--r--tests/conversion_imgs.tar.xzbin0 -> 8135164 bytes
-rw-r--r--tests/crypto-vectors.c1559
-rw-r--r--tests/cryptsetup-valg-supps14
-rwxr-xr-xtests/device-test363
-rw-r--r--tests/differ.c166
-rwxr-xr-xtests/discards-test113
-rw-r--r--tests/evil_hdr-keyslot_overlap.xzbin0 -> 676 bytes
-rw-r--r--tests/evil_hdr-luks_hdr_damage.xzbin0 -> 612 bytes
-rw-r--r--tests/evil_hdr-payload_overwrite.xzbin0 -> 612 bytes
-rw-r--r--tests/evil_hdr-small_luks_device.xzbin0 -> 452 bytes
-rw-r--r--tests/evil_hdr-stripes_payload_dmg.xzbin0 -> 616 bytes
-rw-r--r--tests/fake_systemd_tpm_path.c17
-rw-r--r--tests/fake_token_path.c6
-rw-r--r--tests/fuzz/FuzzerInterface.h81
-rw-r--r--tests/fuzz/LUKS2.proto379
-rw-r--r--tests/fuzz/LUKS2_plain_JSON.proto190
-rw-r--r--tests/fuzz/Makefile.am122
-rw-r--r--tests/fuzz/README.md66
-rw-r--r--tests/fuzz/crypt2_load_fuzz.cc112
-rw-r--r--tests/fuzz/crypt2_load_fuzz.dict130
-rw-r--r--tests/fuzz/crypt2_load_ondisk_fuzz.cc64
-rw-r--r--tests/fuzz/crypt2_load_ondisk_fuzz.dict9
-rw-r--r--tests/fuzz/crypt2_load_proto_fuzz.cc51
-rw-r--r--tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc51
-rw-r--r--tests/fuzz/crypt2_load_proto_plain_json_fuzz.dict72
-rw-r--r--tests/fuzz/json_proto_converter.cc87
-rw-r--r--tests/fuzz/json_proto_converter.h43
-rwxr-xr-xtests/fuzz/oss-fuzz-build.sh152
-rw-r--r--tests/fuzz/plain_json_proto_to_luks2.cc75
-rw-r--r--tests/fuzz/plain_json_proto_to_luks2_converter.cc153
-rw-r--r--tests/fuzz/plain_json_proto_to_luks2_converter.h58
-rw-r--r--tests/fuzz/proto_to_luks2.cc75
-rw-r--r--tests/fuzz/proto_to_luks2_converter.cc604
-rw-r--r--tests/fuzz/proto_to_luks2_converter.h91
-rw-r--r--tests/fuzz/unpoison-mutated-buffers-from-libfuzzer.patch29
-rwxr-xr-xtests/fvault2-compat-test134
-rw-r--r--tests/fvault2-images.tar.xzbin0 -> 34200 bytes
-rwxr-xr-xtests/generate-symbols-list35
-rwxr-xr-xtests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh44
-rwxr-xr-xtests/generators/generate-luks2-argon2-leftover-params.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-correct-full-json0.img.sh58
-rwxr-xr-xtests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh32
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-hdr0.img.sh28
-rwxr-xr-xtests/generators/generate-luks2-invalid-checksum-hdr1.img.sh29
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c0.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c1.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-invalid-json-size-c2.img.sh55
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-invalid-object-type-json0.img.sh41
-rwxr-xr-xtests/generators/generate-luks2-invalid-opening-char-json0.img.sh41
-rwxr-xr-xtests/generators/generate-luks2-invalid-tokens.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-invalid-top-objects.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-keyslot-invalid-af.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-keyslot-invalid-area-size.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-keyslot-invalid-area.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-keyslot-invalid-objects.img.sh41
-rwxr-xr-xtests/generators/generate-luks2-keyslot-missing-digest.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-keyslot-too-many-digests.img.sh41
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-128k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-128k.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-16k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-1m-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-1m.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-256k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-256k.img.sh62
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-2m-secondary.img.sh63
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-2m.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-32k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-32k.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-4m-secondary.img.sh63
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-4m.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-512k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-512k.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh63
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh63
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k-secondary.img.sh64
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-64k.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh63
-rwxr-xr-xtests/generators/generate-luks2-metadata-size-invalid.img.sh61
-rwxr-xr-xtests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh45
-rwxr-xr-xtests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh45
-rwxr-xr-xtests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh60
-rwxr-xr-xtests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh43
-rwxr-xr-xtests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh47
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh42
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-offset.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-size.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-missing-type.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-two.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-unknown-type.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-backup-key-0.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-backup-key-1.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-flags-element.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-flags.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-offset.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-0.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-1.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-size-2.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-segment-wrong-type.img.sh39
-rwxr-xr-xtests/generators/generate-luks2-uint64-max-segment-size.img.sh40
-rwxr-xr-xtests/generators/generate-luks2-uint64-overflow-segment-size.img.sh38
-rwxr-xr-xtests/generators/generate-luks2-uint64-signed-segment-size.img.sh39
-rw-r--r--tests/generators/lib.sh283
-rw-r--r--tests/img_fs_ext4.img.xzbin0 -> 5680 bytes
-rw-r--r--tests/img_fs_vfat.img.xzbin0 -> 6124 bytes
-rw-r--r--tests/img_fs_xfs.img.xzbin0 -> 7676 bytes
-rwxr-xr-xtests/integrity-compat-test651
-rwxr-xr-xtests/keyring-compat-test234
-rwxr-xr-xtests/keyring-test238
-rwxr-xr-xtests/loopaes-test192
-rwxr-xr-xtests/luks1-compat-test124
-rw-r--r--tests/luks1-images.tar.xzbin0 -> 67192 bytes
-rwxr-xr-xtests/luks2-integrity-test203
-rwxr-xr-xtests/luks2-reencryption-mangle-test548
-rwxr-xr-xtests/luks2-reencryption-test2207
-rwxr-xr-xtests/luks2-validation-test249
-rw-r--r--tests/luks2_header_requirements.tar.xzbin0 -> 134412 bytes
-rw-r--r--tests/luks2_keyslot_unassigned.img.xzbin0 -> 270548 bytes
-rw-r--r--tests/luks2_mda_images.tar.xzbin0 -> 156028 bytes
-rw-r--r--tests/luks2_valid_hdr.img.xzbin0 -> 3684 bytes
-rwxr-xr-xtests/mode-test187
-rwxr-xr-xtests/password-hash-test213
-rwxr-xr-xtests/reencryption-compat-test489
-rwxr-xr-xtests/run-all-symbols21
-rwxr-xr-xtests/ssh-test-plugin204
-rwxr-xr-xtests/systemd-test-plugin150
-rwxr-xr-xtests/tcrypt-compat-test223
-rw-r--r--tests/tcrypt-images.tar.xzbin0 -> 308700 bytes
-rw-r--r--tests/test_utils.c950
-rw-r--r--tests/unit-utils-crypt.c259
-rw-r--r--tests/unit-utils-io.c352
-rwxr-xr-xtests/unit-wipe-test170
-rw-r--r--tests/unit-wipe.c138
-rwxr-xr-xtests/valg-api.sh11
-rwxr-xr-xtests/valg.sh11
-rw-r--r--tests/valid_header_file.xzbin0 -> 608 bytes
-rwxr-xr-xtests/verity-compat-test559
-rw-r--r--tests/xfs_512_block_size.img.xzbin0 -> 6284 bytes
176 files changed, 30050 insertions, 0 deletions
diff --git a/tests/00modules-test b/tests/00modules-test
new file mode 100755
index 0000000..f816b80
--- /dev/null
+++ b/tests/00modules-test
@@ -0,0 +1,49 @@
+#!/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 -m
+
+pversion cryptsetup
+pversion veritysetup
+pversion integritysetup
+
+[ -x $CRYPTSETUP_PATH/cryptsetup ] && {
+ echo -e "Cryptsetup defaults:"
+ $CRYPTSETUP_PATH/cryptsetup --help | sed -n '/optional key file for/,$p' | tail -n +3
+}
+
+[ $(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..c8a46a8
--- /dev/null
+++ b/tests/Makefile.am
@@ -0,0 +1,206 @@
+TESTS = 00modules-test \
+ api-test \
+ api-test-2 \
+ compat-args-test \
+ 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-test \
+ bitlk-compat-test \
+ fvault2-compat-test \
+ run-all-symbols \
+ unit-utils-crypt-test \
+ unit-wipe-test \
+ reencryption-compat-test \
+ luks2-reencryption-test \
+ luks2-reencryption-mangle-test
+
+if VERITYSETUP
+TESTS += verity-compat-test
+endif
+
+if INTEGRITYSETUP
+TESTS += integrity-compat-test
+endif
+
+if SSHPLUGIN_TOKEN
+TESTS += ssh-test-plugin
+endif
+
+if EXTERNAL_TOKENS
+TESTS += systemd-test-plugin
+endif
+
+ssh-test-plugin: fake_token_path.so
+systemd-test-plugin: fake_token_path.so fake_systemd_tpm_path.so
+
+# Do not use global CFLAGS here as the *.so link does not support sanitizers
+fake_token_path.so: fake_token_path.c
+ $(CC) $(LDFLAGS) -I $(top_srcdir)/lib -fPIC -shared -D_GNU_SOURCE \
+ -Wl,--version-script=$(top_srcdir)/lib/libcryptsetup.sym \
+ -o fake_token_path.so $(top_srcdir)/tests/fake_token_path.c \
+ -DBUILD_DIR=\"$(abs_top_srcdir)/.libs/\"
+
+fake_systemd_tpm_path.so: fake_systemd_tpm_path.c
+ $(CC) $(LDFLAGS) -fPIC -shared -D_GNU_SOURCE -o fake_systemd_tpm_path.so \
+ $(top_srcdir)/tests/fake_systemd_tpm_path.c
+
+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 \
+ xfs_512_block_size.img.xz \
+ valid_header_file.xz \
+ luks2_valid_hdr.img.xz \
+ luks2_header_requirements.tar.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-args-test \
+ compat-test \
+ compat-test2 \
+ loopaes-test align-test discards-test mode-test password-hash-test \
+ align-test2 verity-compat-test \
+ reencryption-compat-test \
+ 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-test \
+ blkid-luks2-pv.img.xz \
+ Makefile.localtest \
+ bitlk-compat-test \
+ bitlk-images.tar.xz \
+ fvault2-compat-test \
+ fvault2-images.tar.xz \
+ ssh-test-plugin \
+ generate-symbols-list \
+ run-all-symbols \
+ fake_token_path.c \
+ fake_systemd_tpm_path.c \
+ unit-wipe-test \
+ systemd-test-plugin
+
+CLEANFILES = cryptsetup-tst* valglog* *-fail-*.log test-symbols-list.h fake_token_path.so fake_systemd_tpm_path.so
+clean-local:
+ -rm -rf tcrypt-images luks1-images luks2-images bitlk-images fvault2-images conversion_imgs luks2_valid_hdr.img blkid-luks2-pv-img blkid-luks2-pv-img.bcp external-tokens
+
+differ_SOURCES = differ.c
+differ_CFLAGS = $(AM_CFLAGS) -Wall -O2
+
+api_test_SOURCES = api-test.c api_test.h test_utils.c
+api_test_LDADD = ../libcryptsetup.la
+api_test_LDFLAGS = $(AM_LDFLAGS) -static
+api_test_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib
+api_test_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+api_test_2_SOURCES = api-test-2.c api_test.h test_utils.c
+api_test_2_LDADD = ../libcryptsetup.la
+api_test_2_LDFLAGS = $(AM_LDFLAGS) -static
+api_test_2_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib
+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_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
+
+unit_utils_crypt_test_SOURCES = unit-utils-crypt.c ../lib/utils_crypt.c ../lib/utils_crypt.h
+unit_utils_crypt_test_LDADD = ../libcryptsetup.la
+unit_utils_crypt_test_LDFLAGS = $(AM_LDFLAGS) -static
+unit_utils_crypt_test_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib
+unit_utils_crypt_test_CPPFLAGS = $(AM_CPPFLAGS) -include config.h
+
+unit_wipe_SOURCES = unit-wipe.c
+unit_wipe_LDADD = ../libcryptsetup.la
+unit_wipe_LDFLAGS = $(AM_LDFLAGS) -static
+unit_wipe_CFLAGS = $(AM_CFLAGS) -I$(top_srcdir)/lib
+unit_wipe_CPPFLAGS = $(AM_CPPFLAGS)
+
+BUILT_SOURCES = test-symbols-list.h
+
+test-symbols-list.h: $(top_srcdir)/lib/libcryptsetup.sym generate-symbols-list
+ $(srcdir)/generate-symbols-list $(top_srcdir)/lib/libcryptsetup.sym > $(builddir)/test-symbols-list.h
+
+all_symbols_test_SOURCES = all-symbols-test.c
+nodist_all_symbols_test_SOURCES = test-symbols-list.h
+all_symbols_test.$(OBJEXT): test-symbols-list.h
+all_symbols_test_LDFLAGS = $(AM_LDFLAGS) -ldl
+all_symbols_test_CFLAGS = $(AM_CFLAGS)
+all_symbols_test_CPPFLAGS = $(AM_CPPFLAGS) -D_GNU_SOURCE
+
+check_PROGRAMS = api-test api-test-2 differ vectors-test unit-utils-io unit-utils-crypt-test unit-wipe all-symbols-test
+
+check-programs: test-symbols-list.h $(check_PROGRAMS) fake_token_path.so fake_systemd_tpm_path.so
+
+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-args-test
+ @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
+ @VALG=1 ./tcrypt-compat-test
+ @VALG=1 ./align-test
+ @VALG=1 ./align-test2
+ @VALG=1 ./device-test
+ @VALG=1 ./discards-test
+ @VALG=1 ./keyring-compat-test
+ @VALG=1 ./loopaes-test
+ @VALG=1 ./luks1-compat-test
+ @VALG=1 ./luks2-integrity-test
+ @VALG=1 ./mode-test
+ @VALG=1 ./password-hash-test
+ @VALG=1 ./reencryption-compat-test
+ @VALG=1 ./fvault2-compat-test
+ @[ -z "$RUN_SSH_PLUGIN_TEST" ] || VALG=1 ./ssh-test-plugin
+ @INFOSTRING="unit-utils-crypt-test" ./valg-api.sh ./unit-utils-crypt-test
+ @INFOSTRING="vectors-test" ./valg-api.sh ./vectors-test
+ @grep -l "ERROR SUMMARY: [^0][0-9]* 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..89ce2c3
--- /dev/null
+++ b/tests/Makefile.localtest
@@ -0,0 +1,66 @@
+#
+# Makefile to run tests with system binaries
+# USE: make -f Makefile.localtest tests CRYPTSETUP_PATH=/sbin
+# (append TESTSUITE_NOSKIP=y to avoid treating skipped tests as success)
+#
+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 -D_GNU_SOURCE
+LDLIBS=-lcryptsetup -ldevmapper
+TESTS=$(wildcard *-test *-test2) api-test api-test-2 all-symbols-test unit-utils-crypt-test
+TESTS_UTILS=differ unit-utils-io unit-wipe
+
+ifneq ($(RUN_SSH_PLUGIN_TEST),)
+TESTS += ssh-test-plugin
+endif
+
+ifneq ($(RUN_SYSTEMD_PLUGIN_TEST),)
+TESTS += systemd-test-plugin
+TESTS_UTILS += fake_systemd_tpm_path.so
+endif
+
+check-programs: $(TESTS_UTILS) $(TESTS)
+
+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)
+
+unit-wipe: unit-wipe.o
+ $(CC) -o $@ $^ $(LDLIBS)
+
+unit-utils-io: unit-utils-io.o ../lib/utils_io.o
+ $(CC) -o $@ $^
+
+unit-utils-crypt-test: unit-utils-crypt.o ../lib/utils_crypt.o
+ $(CC) -o $@ $^ $(LDLIBS)
+
+test-symbols-list.h: generate-symbols-list
+ ./generate-symbols-list ../lib/libcryptsetup.sym > test-symbols-list.h
+
+all-symbols-test.o: test-symbols-list.h
+ $(CC) -c $*.c
+
+all-symbols-test: all-symbols-test.o
+ $(CC) -o $@ $^ -ldl
+
+fake_systemd_tpm_path.so: fake_systemd_tpm_path.c
+ $(CC) -fPIC -shared -D_GNU_SOURCE -o fake_systemd_tpm_path.so fake_systemd_tpm_path.c
+
+tests: $(TESTS_UTILS) $(TESTS)
+ @for test in $(sort $(TESTS)); do \
+ echo [$$test]; \
+ ./$$test; \
+ [ $(if $(TESTSUITE_NOSKIP),,$$? -ne 77 -a) $$? -ne 0 ] && exit 1; \
+ true; \
+ done;
+
+clean:
+ rm -f *.o $(TESTS_UTILS) api-test api-test-2 unit-utils-crypt-test \
+ all-symbols-test test-symbols-list.h
+
+.PHONY: clean
diff --git a/tests/align-test b/tests/align-test
new file mode 100755
index 0000000..5941cde
--- /dev/null
+++ b/tests/align-test
@@ -0,0 +1,341 @@
+#!/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"
+
+FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+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 >/dev/null 2>&1
+ 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 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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_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 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 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 --new-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 --new-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
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+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 >/dev/null 2>&1 || skip "This kernel seems to not support proper scsi_debug module, test skipped."
+cleanup
+if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+fi
+
+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
+
+# skip tests using empty passphrase (LUKS1 cipher_null)
+if [ ! fips_mode ]; then
+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 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 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
+fi
+
+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..33126a4
--- /dev/null
+++ b/tests/align-test2
@@ -0,0 +1,449 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+DEV=""
+DEV_STACKED="luks0xbabe"
+DEV_NAME="dummyalign"
+HDR="test_luks2_hdr"
+MNT_DIR="./mnt_luks"
+PWD1="93R4P4pIqAH8"
+PWD2="mymJeD8ivEhE"
+FAST_PBKDF="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+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 >/dev/null 2>&1
+ sleep 1
+ rm -f $HDR 2>/dev/null
+}
+
+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 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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_features()
+{
+ VER_STR=$(dmsetup targets | grep crypt | cut -f2 -dv)
+ [ -z "$VER_STR" ] && fail "Failed to parse dm-crypt version."
+
+ VER_MAJ=$(echo $VER_STR | cut -f 1 -d.)
+ VER_MIN=$(echo $VER_STR | cut -f 2 -d.)
+ VER_PTC=$(echo $VER_STR | cut -f 3 -d.)
+
+ [ $VER_MAJ -lt 1 ] && return
+ [ $VER_MAJ -gt 1 ] && {
+ DM_PERF_CPU=1
+ DM_SECTOR_SIZE=1
+ return
+ }
+
+ [ $VER_MIN -lt 14 ] && return
+ DM_PERF_CPU=1
+ if [ $VER_MIN -ge 17 ]; then
+ DM_SECTOR_SIZE=1
+ fi
+}
+
+add_device() {
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 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 | 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 --new-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"
+}
+
+auto_sector() # expected device header
+{
+ local _exp=$1
+ local _dev=$2
+ local _hdr=$2
+ local _hdrstr=""
+ local _hdrmsg=""
+
+ if [ -n "$3" ]; then
+ _hdrstr="--header $3"
+ _hdr=$3
+ _hdrmsg=" detached header"
+ fi
+
+ echo -n "Formatting$_hdrmsg using optimal encryption sector size (expecting $_exp)..."
+
+ if [ -z "$DM_SECTOR_SIZE" -a $_exp -ne 512 ]; then
+ echo "SKIPPED"
+ return
+ fi
+
+ echo $PWD1 | $CRYPTSETUP luksFormat $FAST_PBKDF --type luks2 $_hdrstr $_dev -q >/dev/null 2>&1 || fail
+
+ # check the device can be activated
+ echo $PWD1 | $CRYPTSETUP luksOpen $_hdrstr $_dev $DEV_NAME || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ SECTOR=$($CRYPTSETUP luksDump $_hdr | grep -A4 "0: crypt" | grep "sector:" | cut -d ' ' -f2)
+
+ [ -z "$SECTOR" ] && fail
+ [ $SECTOR -ne $_exp ] && fail "Expected sector size differs: expected $_exp != detected $SECTOR"
+
+ echo "PASSED"
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+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 >/dev/null 2>&1 || skip "This kernel seems to not support proper scsi_debug module, test skipped."
+cleanup
+if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+fi
+
+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
+
+echo "# Create classic 512B drive"
+echo "# (logical_block_size=512, physical_block_size=512, alignment_offset=0)"
+add_device dev_size_mb=32 sector_size=512 num_tgts=1
+auto_sector 512 $DEV
+auto_sector 512 $DEV $HDR
+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
+auto_sector 4096 $DEV
+auto_sector 4096 $DEV $HDR
+DEV2=$DEV
+DEV=/dev/mapper/$DEV_STACKED
+dmsetup create $DEV_STACKED --table "0 $((`blockdev --getsz $DEV2`-1)) linear $DEV2 0"
+auto_sector 512 $DEV
+auto_sector 512 $DEV $HDR
+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
+auto_sector 4096 $DEV
+auto_sector 4096 $DEV $HDR
+cleanup
diff --git a/tests/all-symbols-test.c b/tests/all-symbols-test.c
new file mode 100644
index 0000000..10c7fe2
--- /dev/null
+++ b/tests/all-symbols-test.c
@@ -0,0 +1,177 @@
+/*
+ * Test utility checking symbol versions in libcryptsetup.
+ *
+ * Copyright (C) 2021-2023 Red Hat, Inc. All rights reserved.
+ *
+ * This file is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This file 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this file; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <dlfcn.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define UNUSED(expr) do { (void)(expr); } while (0)
+
+static int _debug;
+static const char *libfile = "libcryptsetup.so.12";
+
+#define LOG_MAX_LEN 256
+
+#define LOG_DEBUG 1
+#define LOG_NORMAL 2
+#define LOG_ERROR 3
+
+__attribute__((format(printf, 2, 3)))
+static void test_logf(int level, const char *format, ...)
+{
+ va_list argp;
+ char target[LOG_MAX_LEN + 2];
+ int len;
+
+ va_start(argp, format);
+
+ len = vsnprintf(&target[0], LOG_MAX_LEN, format, argp);
+ if (len > 0 && len < LOG_MAX_LEN) {
+ switch (level) {
+ case LOG_DEBUG:
+ if (!_debug)
+ break;
+ /* fall through */
+ case LOG_NORMAL:
+ fprintf(stdout, "%s", target);
+ break;
+ case LOG_ERROR:
+ fflush(stdout);
+ strcat(target, "\n");
+ fprintf(stderr, "%s", target);
+ }
+ }
+
+ va_end(argp);
+}
+
+#define log_dbg(x...) test_logf(LOG_DEBUG, x)
+#define log_std(x...) test_logf(LOG_NORMAL, x)
+#define log_err(x...) test_logf(LOG_ERROR, x)
+
+static int check_dlvsym(void *h, const char *symbol, const char *version)
+{
+#ifdef HAVE_DLVSYM
+ void *sym;
+ char *err;
+
+ log_dbg("Checking %s@%s...", symbol, version);
+ sym = dlvsym(h, symbol, version);
+ UNUSED(sym);
+ err = dlerror();
+
+ if (err) {
+ log_err("%s.", err);
+ return 1;
+ }
+
+ log_dbg("OK\n");
+#endif
+ return 0;
+}
+
+static int check_dlsym(void *h, const char *symbol)
+{
+ void *sym;
+ char *err;
+
+ log_dbg("Checking %s...", symbol);
+ sym = dlsym(h, symbol);
+ UNUSED(sym);
+ err = dlerror();
+
+ if (err) {
+ log_err("%s", err);
+ return 1;
+ }
+
+ log_dbg("OK\n");
+ return 0;
+}
+
+static int check_all_symbols(void *h)
+{
+ unsigned scount = 0;
+
+#define CHECK_SYMBOL(SYM, VER) \
+do { \
+ if (check_dlvsym(h, #SYM, #VER)) \
+ return 1; \
+ if (check_dlsym(h, #SYM)) \
+ return 1; \
+ scount++; \
+} while (0);
+
+#include "test-symbols-list.h"
+#undef CHECK_SYMBOL
+
+ if (!scount) {
+ log_err("test-symbols-list.h file is probably empty.");
+ return 1;
+ }
+
+ log_std("Performed %u symbol checks in total.\n", scount);
+
+ return 0;
+}
+
+static void usage(const char *app)
+{
+ log_std("usage:\n\t%s [-v|--verbose|--debug] [optional path to library so file]\n", app);
+
+ exit(EXIT_FAILURE);
+}
+
+int main(int argc, char **argv)
+{
+ int i, r;
+ void *h;
+
+ for (i = 1; i < argc; i++) {
+ if (*argv[i] != '-')
+ libfile = argv[i];
+ else if (!strcmp("-v", argv[i]) || !strcmp("--verbose", argv[i]) ||
+ !strcmp("--debug", argv[i]))
+ _debug = 1;
+ else if (!strcmp("-h", argv[i]) || !strcmp("--help", argv[i]))
+ usage(argv[0]);
+ }
+
+ log_std("Checking dlopen(%s)...", libfile);
+
+ h = dlopen(libfile, RTLD_NOW);
+ if (!h) {
+ log_err("dlopen(): %s.", dlerror());
+ return EXIT_FAILURE;
+ }
+ dlerror();
+ log_std("OK\n");
+
+ r = check_all_symbols(h);
+
+ if (dlclose(h)) {
+ log_err("Failed to dlclose %s: %s.", libfile, dlerror());
+ return EXIT_FAILURE;
+ }
+
+ return r ? EXIT_FAILURE : EXIT_SUCCESS;
+}
diff --git a/tests/api-test-2.c b/tests/api-test-2.c
new file mode 100644
index 0000000..824ae65
--- /dev/null
+++ b/tests/api-test-2.c
@@ -0,0 +1,5139 @@
+/*
+ * cryptsetup library LUKS2 API check functions
+ *
+ * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2016-2023 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 "luks1/luks.h"
+#include "libcryptsetup.h"
+
+#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 "blablabl"
+#define PASSPHRASE1 "albalbal"
+
+#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 "aaablabl"
+#define PASS1 "hhhblabl"
+#define PASS2 "cccblabl"
+#define PASS3 "dddblabl"
+#define PASS4 "eeeblabl"
+#define PASS5 "fffblabl"
+#define PASS6 "gggblabl"
+#define PASS7 "bbbblabl"
+#define PASS8 "iiiblabl"
+
+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;
+
+static const char *default_luks1_hash = NULL;
+static uint32_t default_luks1_iter_time = 0;
+
+static const char *default_luks2_pbkdf = NULL;
+static uint32_t default_luks2_iter_time = 0;
+static uint32_t default_luks2_memory_kb = 0;
+static uint32_t default_luks2_parallel_threads = 0;
+
+static struct crypt_pbkdf_type min_pbkdf2 = {
+ .type = "pbkdf2",
+ .iterations = 1000,
+ .flags = CRYPT_PBKDF_NO_BENCHMARK
+}, min_argon2 = {
+ .type = "argon2id",
+ .iterations = 4,
+ .max_memory_kb = 32,
+ .parallel_threads = 1,
+ .flags = CRYPT_PBKDF_NO_BENCHMARK
+};
+
+// 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 (r_header_size)
+ *r_header_size = 0;
+ if (r_payload_offset)
+ *r_payload_offset = 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 bool get_luks_pbkdf_defaults(void)
+{
+ const struct crypt_pbkdf_type *pbkdf_defaults = crypt_get_pbkdf_default(CRYPT_LUKS1);
+
+ if (!pbkdf_defaults)
+ return false;
+
+ default_luks1_hash = pbkdf_defaults->hash;
+ default_luks1_iter_time = pbkdf_defaults->time_ms;
+
+ pbkdf_defaults = crypt_get_pbkdf_default(CRYPT_LUKS2);
+ if (!pbkdf_defaults)
+ return false;
+
+ default_luks2_pbkdf = pbkdf_defaults->type;
+ default_luks2_iter_time = pbkdf_defaults->time_ms;
+ default_luks2_memory_kb = pbkdf_defaults->max_memory_kb;
+ default_luks2_parallel_threads = pbkdf_defaults->parallel_threads;
+
+ return true;
+}
+
+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 (!test_loop_file)
+ return 1;
+
+ if ((fd=mkstemp(test_loop_file)) == -1) {
+ printf("cannot create temporary file with template %s\n", test_loop_file);
+ return 1;
+ }
+ close(fd);
+ if (snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ test_loop_file, TST_SECTOR_SIZE, TST_LOOP_FILE_SIZE) < 0)
+ return 1;
+ 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 (!tmp_file_1)
+ return 1;
+
+ if ((fd=mkstemp(tmp_file_1)) == -1) {
+ printf("cannot create temporary file with template %s\n", tmp_file_1);
+ return 1;
+ }
+ close(fd);
+ if (snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ tmp_file_1, TST_SECTOR_SIZE, 10) < 0)
+ return 1;
+ 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);
+
+ if (t_set_readahead(DEVICE_ERROR, 0))
+ printf("cannot set read ahead on device %s\n", DEVICE_ERROR);
+
+ _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 " ] && tar xJf " REQS_LUKS2_HEADER ".tar.xz", 1);
+ fd = loop_attach(&DEVICE_4, NO_REQS_LUKS2_HEADER, 0, 0, &ro);
+ close(fd);
+
+ _system(" [ ! -e " REQS_LUKS2_HEADER " ] && tar xJf " REQS_LUKS2_HEADER ".tar.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 >/dev/null 2>&1", 1))
+ return 1;
+
+ if (t_dm_check_versions())
+ return 1;
+
+ _system("rmmod dm-crypt >/dev/null 2>&1", 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);
+
+ if (!get_luks_pbkdf_defaults())
+ return 1;
+
+ min_pbkdf2.hash = min_argon2.hash = default_luks1_hash;
+
+ return 0;
+}
+
+static int set_fast_pbkdf(struct crypt_device *cd)
+{
+ const struct crypt_pbkdf_type *pbkdf = &min_argon2;
+
+ /* Cannot use Argon2 in FIPS */
+ if (_fips_mode)
+ pbkdf = &min_pbkdf2;
+
+ return crypt_set_pbkdf_type(cd, pbkdf);
+}
+
+#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 __attribute__((unused)),
+ int token __attribute__((unused)),
+ 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 __attribute__((unused)), 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;
+
+ 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_(set_fast_pbkdf(cd));
+ 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 *tmp_buf, *passphrase = PASSPHRASE, *passphrase2 = "nsdkFI&Y#.sd";
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(vk_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, vk_hex, key_size);
+ crypt_decode_key(key3, vk_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, &params), "Not enough space for keyslots material");
+ CRYPT_FREE(cd);
+
+ // test payload_offset = 0 for encrypted device with external header device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), 0);
+ CRYPT_FREE(cd);
+
+ params.data_alignment = 0;
+ params.data_device = NULL;
+
+ // test payload_offset = 0. format() should look up alignment offset from device topology
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(!(crypt_get_data_offset(cd) > 0));
+ CRYPT_FREE(cd);
+
+ // set_data_offset has priority, alignment must be 0 or must be compatible
+ params.data_alignment = 0;
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_set_data_offset(cd, OFFSET_8M));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ CRYPT_FREE(cd);
+
+ // Load gets the value from metadata
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ CRYPT_FREE(cd);
+
+ params.data_alignment = OFFSET_4M;
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(set_fast_pbkdf(cd));
+ FAIL_(crypt_set_data_offset(cd, OFFSET_2M + 1), "Not aligned to 4096"); // must be aligned to 4k
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Alignment not compatible");
+ OK_(crypt_set_data_offset(cd, OFFSET_4M));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_4M);
+ CRYPT_FREE(cd);
+
+ /*
+ * test limit values for backing device size
+ */
+ params.data_alignment = OFFSET_4M;
+ OK_(get_luks2_offsets(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));
+ OK_(set_fast_pbkdf(cd));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Device too small");
+ CRYPT_FREE(cd);
+
+ // 0 sectors for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small");
+ CRYPT_FREE(cd);
+
+ // 1 sector for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), r_payload_offset);
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1));
+ EQ_(r_size_1, TST_SECTOR_SIZE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // restrict format only to empty context
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted");
+ // change data device to wrong one
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+
+ // generate keyslot material at the end of luks header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7);
+
+ OK_(crypt_keyslot_get_pbkdf(cd, 7, &pbkdf_tmp));
+ OK_(strcmp(pbkdf_tmp.type, pbkdf.type));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params), "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_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(0, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ // there we've got uuid mismatch
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ 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));
+ EQ_(crypt_header_is_detached(cd), 1);
+ CRYPT_FREE(cd);
+
+ params.data_device = NULL;
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+
+ // even with no keyslots defined it can be activated by volume key
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0));
+ 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));
+
+ 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, passphrase2, strlen(passphrase2)));
+ 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_(6, crypt_keyslot_change_by_passphrase(cd, 6, CRYPT_ANY_SLOT, passphrase2, strlen(passphrase2), passphrase, strlen(passphrase)));
+ 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_dump_json(cd, NULL, 42), "flags be used later");
+ OK_(crypt_dump_json(cd, NULL, 0));
+ OK_(!(global_lines != 0));
+ reset_log();
+ OK_(crypt_dump_json(cd, &tmp_buf, 0));
+ OK_(!(tmp_buf && strlen(tmp_buf) != 0));
+
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_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, &params));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, PASSPHRASE, strlen(PASSPHRASE)), 7);
+ CRYPT_FREE(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, 0x080000);
+ EQ_(keyslots_size, 0x080000);
+ CRYPT_FREE(cd);
+ // default
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_get_metadata_size(cd, &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, &params));
+ 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, &params), "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, &params), "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, &params), "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, &params));
+ 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, &params), "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, &params));
+ 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, &params));
+ 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
+ GE_(snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\"", CDEVICE_2, DEVICE_2), 0);
+ _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)
+ GE_(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), 0);
+ _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 = "sha256",
+ .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 *vk_hex = "ccadd99b16cd3d200c22d6db45d8b6630ef3d936767127347ec8a76ab992c2ea";
+ size_t key_size = strlen(vk_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, vk_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, &params));
+ // FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, VALID_LUKS2_HEADER), "Volume keysize mismatch");
+ // CRYPT_FREE(cd);
+
+ // payload offset mismatch
+ params.data_alignment = 8193;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, NO_REQS_LUKS2_HEADER), "Payload offset mismatch");
+ CRYPT_FREE(cd);
+ params.data_alignment = 4096;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ // FIXME: either format has to fail or next line must be true
+ // EQ_(crypt_get_data_offset(cd), params.data_alignment);
+ // FAIL_(crypt_header_restore(cd, CRYPT_LUKS2, VALID_LUKS2_HEADER), "Payload offset mismatch");
+ CRYPT_FREE(cd);
+
+ // do not allow restore over LUKS1 header on device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ 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 = "sha256",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], cmd[256];
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_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));
+ GE_(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), 0);
+ OK_(_system(cmd, 1));
+ // some device
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ // 1 sector device
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_header_size + 1));
+ // 0 sectors device for payload
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_header_size));
+
+ // valid metadata and device size
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_OK;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ 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));
+ EQ_(1, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+
+ // repeat with init with two devices
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ EQ_(1, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+
+ // bad header: device too small (payloadOffset > device_size)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Device too small");
+ NULL_(crypt_get_type(cd));
+ CRYPT_FREE(cd);
+
+ // 0 secs for encrypted data area
+ params.data_alignment = 8192;
+ params.data_device = NULL;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ // load should be ok
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // damaged header
+ OK_(_system("dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 count=8 2>/dev/null", 1));
+ OK_(_system("dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 seek=32 count=8 2>/dev/null", 1));
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Header not found");
+ CRYPT_FREE(cd);
+
+ // plain device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ FAIL_(crypt_load(cd, CRYPT_PLAIN, NULL), "Can't load nonLUKS device type");
+ CRYPT_FREE(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, &pl_params));
+ FAIL_(crypt_load(cd, CRYPT_LUKS2, NULL), "Can't load over nonLUKS device type");
+ CRYPT_FREE(cd);
+
+ //LUKSv2 device
+ OK_(crypt_init(&cd, DEVICE_4));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ CRYPT_FREE(cd);
+ OK_(crypt_init(&cd, DEVICE_4));
+ crypt_set_iteration_time(cd, 0); /* invalid for argon2 pbkdf, ignored */
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ CRYPT_FREE(cd);
+
+ /* check load sets proper device type */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ EQ_(strcmp(CRYPT_LUKS2, crypt_get_type(cd)), 0);
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void Luks2HeaderBackup(void)
+{
+ struct crypt_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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_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, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, passphrase, strlen(passphrase)), 0);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // restore header from backup
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(0, crypt_header_is_detached(cd));
+ 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));
+ EQ_(1, crypt_header_is_detached(cd));
+ 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes", *capi_cipher = "capi:cbc(aes)";
+ const char *cipher_mode = "cbc-essiv:sha256", *capi_cipher_mode = "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, vk_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, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 42));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(42, r_size >> TST_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 >> TST_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 >> TST_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, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 666));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(666, r_size >> TST_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 >> TST_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 >> TST_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 >> TST_SECTOR_SHIFT);
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ // check userspace gets hint volume key must be properly loaded in kernel keyring
+ if (t_dm_crypt_keyring_support())
+ EQ_(crypt_resize(cd, CDEVICE_1, 0), -EPERM);
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ CRYPT_FREE(cd);
+
+ // same as above for handles initialised by name
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ if (t_dm_crypt_keyring_support())
+ EQ_(crypt_resize(cd, CDEVICE_1, 0), -EPERM);
+ else
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+#endif
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, NULL, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+
+ /* create second LUKS2 device */
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_LUKS2, cipher, cipher_mode, crypt_get_uuid(cd), key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ /* do not allow resize of other device */
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ CRYPT_FREE(cd2);
+
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_set_pbkdf_type(cd2, &min_pbkdf2));
+ 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);
+
+ if (t_dm_capi_string_supported()) {
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS2, capi_cipher, capi_cipher_mode, NULL, key, key_size, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 8));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(8, r_size >> TST_SECTOR_SHIFT);
+ 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
+ }, params_invalid = {};
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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);
+ FAIL_(crypt_token_luks2_keyring_set(cd, CRYPT_ANY_TOKEN, &params_invalid), "Invalid key description property.");
+ EQ_(crypt_token_luks2_keyring_set(cd, 3, &params), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 3, 0), 3);
+ CRYPT_FREE(cd);
+
+ // test thread keyring key in token 0
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 3, NULL, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 3, NULL, 0), "already open");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ 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, &params), 1);
+ EQ_(crypt_token_assign_keyslot(cd, 1, 0), 1);
+ CRYPT_FREE(cd);
+
+ // test process keyring key in token 1
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, NULL, 0), "already open");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ 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, &params), 0);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0);
+ EQ_(crypt_token_luks2_keyring_set(cd, 1, &params2), 1);
+ FAIL_(crypt_token_assign_keyslot(cd, 1, 1), "Keyslot 1 doesn't exist");
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+ EQ_(crypt_token_luks2_keyring_set(cd, 0, &params), 0);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 0), 0);
+ EQ_(crypt_token_luks2_keyring_set(cd, 2, &params2), 2);
+ EQ_(crypt_token_assign_keyslot(cd, 2, 1), 2);
+ CRYPT_FREE(cd);
+
+ 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, token_max;
+ const char *dummy;
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+ char passptr[] = PASSPHRASE;
+ char passptr1[] = PASSPHRASE1;
+ struct crypt_active_device cad;
+
+ 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));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, 32, NULL));
+ EQ_(crypt_token_status(cd, -1, NULL), CRYPT_TOKEN_INVALID);
+ EQ_(crypt_token_status(cd, 32, NULL), CRYPT_TOKEN_INVALID);
+ EQ_(crypt_token_status(cd, 0, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_token_status(cd, 31, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 0);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 1, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 1);
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN_JSON_INVALID("\"0\"")), "Token validation failed");
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN_JSON("\"0\"")), 0);
+ EQ_(crypt_token_status(cd, 0, NULL), CRYPT_TOKEN_EXTERNAL);
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr, 0), 0);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr, 0), "already active");
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // write invalid token and verify that validate() can detect it after handler being registered
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN1_JSON_INVALID("\"1\"")), 1);
+ EQ_(crypt_token_status(cd, 1, NULL), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ EQ_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, TEST_TOKEN1_JSON("\"1\"")), 2);
+ EQ_(crypt_token_status(cd, 2, &dummy), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ OK_(strcmp(dummy, "test_token1"));
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, passptr1, 0), "Unknown token handler");
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Unknown token handler");
+ OK_(crypt_token_register(&th3));
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 1, passptr1, 0), "Token validation failed");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // test crypt_token_json_get returns correct token id
+ EQ_(crypt_token_json_get(cd, 2, &dummy), 2);
+
+ // exercise assign/unassign keyslots API
+ EQ_(crypt_token_unassign_keyslot(cd, 2, 1), 2);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Token assigned to no keyslot");
+ EQ_(crypt_token_assign_keyslot(cd, 2, 0), 2);
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr1, 0), "Wrong passphrase");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_token_json_set(cd, 1, NULL), 1);
+ FAIL_(crypt_token_json_get(cd, 1, &dummy), "Token is not there");
+ EQ_(crypt_token_unassign_keyslot(cd, 2, CRYPT_ANY_SLOT), 2);
+ EQ_(crypt_token_unassign_keyslot(cd, 0, CRYPT_ANY_SLOT), 0);
+
+ // various tests related to unassigned keyslot to volume segment
+ EQ_(crypt_keyslot_add_by_key(cd, 3, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1), CRYPT_VOLUME_KEY_NO_SEGMENT), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 2, 0), 2);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 3), 0);
+
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, 0), 0);
+ EQ_(crypt_activate_by_token(cd, NULL, 0, passptr1, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 3);
+ // FIXME: useless error message here (or missing one to be specific)
+ FAIL_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr1, 0), "No volume key available in token keyslots");
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_token_assign_keyslot(cd, 0, 1), 0);
+ OK_(crypt_token_is_assigned(cd, 0, 1));
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 0, passptr1, 0), 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ EQ_(crypt_token_assign_keyslot(cd, 2, 3), 2);
+ OK_(crypt_token_is_assigned(cd, 2, 3));
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, 0), 0);
+ EQ_(crypt_activate_by_token(cd, CDEVICE_1, 2, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+#ifdef KERNEL_KEYRING
+ if (t_dm_crypt_keyring_support()) {
+ EQ_(crypt_activate_by_token(cd, NULL, 2, passptr, CRYPT_ACTIVATE_KEYRING_KEY), 0);
+ OK_(_volume_key_in_keyring(cd, 0));
+ }
+ OK_(crypt_volume_key_keyring(cd, 0));
+#endif
+ FAIL_(crypt_activate_by_token(cd, NULL, 2, passptr, CRYPT_ACTIVATE_KEYRING_KEY), "Can't use keyring when disabled in library");
+ OK_(crypt_volume_key_keyring(cd, 1));
+
+ EQ_(crypt_token_luks2_keyring_set(cd, 5, &params), 5);
+ EQ_(crypt_token_status(cd, 5, &dummy), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(dummy, "luks2-keyring"));
+
+ FAIL_(crypt_token_luks2_keyring_get(cd, 2, &params), "Token is not luks2-keyring type");
+
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, BOGUS_TOKEN0_JSON), "luks2- reserved prefix.");
+ FAIL_(crypt_token_json_set(cd, CRYPT_ANY_TOKEN, BOGUS_TOKEN1_JSON), "luks2- reserved prefix.");
+
+ // test we can use crypt_token_json_set for valid luks2-keyring token
+ FAIL_(crypt_token_json_set(cd, 12, LUKS2_KEYRING_TOKEN_JSON_BAD("\"0\"", "\"my_desc_x\"")), "Strict luks2-keyring token validation failed");
+ EQ_(crypt_token_status(cd, 12, NULL), CRYPT_TOKEN_INACTIVE);
+ FAIL_(crypt_token_json_set(cd, 12, LUKS2_KEYRING_TOKEN_JSON("\"5\"", "\"my_desc\"")), "Missing keyslot 5.");
+ EQ_(crypt_token_json_set(cd, 10, LUKS2_KEYRING_TOKEN_JSON("\"1\"", "\"my_desc\"")), 10);
+ EQ_(crypt_token_status(cd, 10, &dummy), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(dummy, "luks2-keyring"));
+ params.key_description = NULL;
+ EQ_(crypt_token_luks2_keyring_get(cd, 10, &params), 10);
+ OK_(strcmp(params.key_description, "my_desc"));
+
+ OK_(crypt_token_is_assigned(cd, 10, 1));
+ // unassigned tests
+ EQ_(crypt_token_is_assigned(cd, 10, 21), -ENOENT);
+ EQ_(crypt_token_is_assigned(cd, 21, 1), -ENOENT);
+ // wrong keyslot or token id tests
+ EQ_(crypt_token_is_assigned(cd, -1, 1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 32, 1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 10, -1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 10, 32), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, -1, -1), -EINVAL);
+ EQ_(crypt_token_is_assigned(cd, 32, 32), -EINVAL);
+
+ // 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);
+
+ // test token activation respects keyslot priorities
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(set_fast_pbkdf(cd));
+ 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, 3, NULL, 32, PASSPHRASE, strlen(PASSPHRASE), CRYPT_VOLUME_KEY_NO_SEGMENT), 3);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 5, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 5);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 8, NULL, 32, PASSPHRASE1, strlen(PASSPHRASE1)), 8);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 12, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 12);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 21, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 21);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 31, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 31);
+
+ OK_(crypt_keyslot_set_priority(cd, 0, CRYPT_SLOT_PRIORITY_IGNORE));
+ OK_(crypt_keyslot_set_priority(cd, 3, CRYPT_SLOT_PRIORITY_PREFER));
+ OK_(crypt_keyslot_set_priority(cd, 8, CRYPT_SLOT_PRIORITY_PREFER));
+ OK_(crypt_keyslot_set_priority(cd, 12,CRYPT_SLOT_PRIORITY_PREFER));
+
+ // expected unusable with CRYPT_ANY_TOKEN
+ EQ_(crypt_token_json_set(cd, 1, TEST_TOKEN_JSON("\"0\", \"3\"")), 1);
+
+ // expected unusable (-EPERM)
+ EQ_(crypt_token_json_set(cd, 5, TEST_TOKEN_JSON("\"8\"")), 5);
+
+ // expected unusable (-EPERM)
+ EQ_(crypt_token_json_set(cd, 4, TEST_TOKEN_JSON("\"8\", \"3\"")), 4);
+
+ // expected unusable (-ENOENT)
+ EQ_(crypt_token_json_set(cd, 6, TEST_TOKEN_JSON("\"3\"")), 6);
+
+ // expected unusable (-ENOENT)
+ EQ_(crypt_token_json_set(cd, 11, TEST_TOKEN_JSON("")), 11);
+
+ token_max = crypt_token_max(CRYPT_LUKS2) - 1;
+ GE_(token_max, 0);
+
+ // expected to be used first with CRYPT_ANY_TOKEN (unlocks with high priority ks 12)
+ EQ_(crypt_token_json_set(cd, token_max, TEST_TOKEN_JSON("\"12\", \"0\", \"3\"")), token_max);
+
+ // expected usable with CRYPT_ANY_TOKEN
+ EQ_(crypt_token_json_set(cd, 8, TEST_TOKEN_JSON("\"5\", \"0\", \"3\"")), 8);
+
+ // of all tokens keyslot 12 has highest priority now
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", CRYPT_ANY_TOKEN, NULL, 0, passptr, 0), 12);
+ EQ_(crypt_activate_by_token_pin(cd, CDEVICE_1, "test_token", CRYPT_ANY_TOKEN, NULL, 0, passptr, 0), 12);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // with explicit token priority ignore may be used
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 1, NULL, 0, passptr, 0), 0);
+ EQ_(crypt_activate_by_token_pin(cd, CDEVICE_1, "test_token", 1, NULL, 0, passptr, 0), 0);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ EQ_(crypt_token_json_set(cd, token_max, NULL), token_max);
+
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", CRYPT_ANY_TOKEN, NULL, 0, passptr, 0), 5);
+
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 5, NULL, 0, passptr, 0), -EPERM);
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 4, NULL, 0, passptr, 0), -EPERM);
+
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 6, NULL, 0, passptr, 0), -ENOENT);
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 6, NULL, 0, passptr, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), 3);
+
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 11, NULL, 0, passptr, 0), -ENOENT);
+ EQ_(crypt_activate_by_token_pin(cd, NULL, "test_token", 11, NULL, 0, passptr, CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY), -ENOENT);
+
+ // test crypt_resume_by_token_pin
+ EQ_(crypt_activate_by_token_pin(cd, CDEVICE_1, "test_token", CRYPT_ANY_TOKEN, NULL, 0, passptr, 0), 5);
+ OK_(crypt_suspend(cd, CDEVICE_1));
+ EQ_(crypt_resume_by_token_pin(cd, CDEVICE_1, "test_token", CRYPT_ANY_TOKEN, NULL, 0, passptr), 5);
+ 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);
+
+ EQ_(crypt_token_max(CRYPT_LUKS2), 32);
+ FAIL_(crypt_token_max(CRYPT_LUKS1), "No token support in LUKS1");
+ FAIL_(crypt_token_max(NULL), "No LUKS format specified");
+ _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 = "sha256",
+ .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));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ 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));
+ OK_(crypt_set_pbkdf_type(cd, &min_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, &min_pbkdf2));
+ 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 = "sha256",
+ .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, &params));
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(crypt_get_pbkdf_type(cd));
+ CRYPT_FREE(cd);
+
+ // test LUKSv1 device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, NULL));
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Unsupported with non-LUKS2 devices");
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, default_luks1_iter_time);
+ CRYPT_FREE(cd);
+ // test value set in crypt_set_iteration_time() can be obtained via following crypt_get_pbkdf_type()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 42);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 42);
+ // test crypt_get_pbkdf_type() returns expected values for LUKSv1
+ OK_(strcmp(pbkdf->type, CRYPT_KDF_PBKDF2));
+ OK_(strcmp(pbkdf->hash, default_luks1_hash));
+ EQ_(pbkdf->max_memory_kb, 0);
+ EQ_(pbkdf->parallel_threads, 0);
+ crypt_set_iteration_time(cd, 43);
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 43);
+ CRYPT_FREE(cd);
+ // test whether crypt_get_pbkdf_type() after double crypt_load()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ crypt_set_iteration_time(cd, 42);
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->time_ms, 42);
+ CRYPT_FREE(cd);
+ // test whether hash passed via *params in crypt_load() has higher priority
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, mode, NULL, NULL, 32, &luks1));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->hash, luks1.hash));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->hash, luks1.hash));
+ CRYPT_FREE(cd);
+
+ // test LUKSv2 device
+ // test default values are set
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, mode, NULL, NULL, 32, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, default_luks2_pbkdf));
+ OK_(strcmp(pbkdf->hash, default_luks1_hash));
+ EQ_(pbkdf->time_ms, default_luks2_iter_time);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), default_luks2_parallel_threads));
+ // set and verify argon2 type
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, argon2.type));
+ OK_(strcmp(pbkdf->hash, argon2.hash));
+ EQ_(pbkdf->time_ms, argon2.time_ms);
+ EQ_(pbkdf->max_memory_kb, argon2.max_memory_kb);
+ EQ_(pbkdf->parallel_threads, argon2.parallel_threads);
+ // set and verify pbkdf2 type
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, pbkdf2.type));
+ OK_(strcmp(pbkdf->hash, pbkdf2.hash));
+ EQ_(pbkdf->time_ms, pbkdf2.time_ms);
+ EQ_(pbkdf->max_memory_kb, pbkdf2.max_memory_kb);
+ EQ_(pbkdf->parallel_threads, pbkdf2.parallel_threads);
+ // reset and verify default values
+ crypt_set_iteration_time(cd, 1); // it's supposed to override this call
+ OK_(crypt_set_pbkdf_type(cd, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, default_luks2_pbkdf));
+ OK_(strcmp(pbkdf->hash, default_luks1_hash));
+ EQ_(pbkdf->time_ms, default_luks2_iter_time);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), default_luks2_parallel_threads));
+ // try to pass illegal values
+ argon2.parallel_threads = 0;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Parallel threads can't be 0");
+ argon2.parallel_threads = 1;
+ argon2.max_memory_kb = 0;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Memory can't be 0");
+ argon2.max_memory_kb = 1024;
+ pbkdf2.parallel_threads = 1;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Parallel threads can't be set with pbkdf2 type");
+ pbkdf2.parallel_threads = 0;
+ pbkdf2.max_memory_kb = 512;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Memory can't be set with pbkdf2 type");
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Unknown type member");
+ bad.type = CRYPT_KDF_PBKDF2;
+ bad.hash = NULL;
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Hash member is empty");
+ bad.type = NULL;
+ bad.hash = default_luks1_hash;
+ FAIL_(crypt_set_pbkdf_type(cd, &bad), "Pbkdf type member is empty");
+ bad.hash = "hamster_hash";
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Unknown hash member");
+ CRYPT_FREE(cd);
+ // test whether crypt_get_pbkdf_type() behaves accordingly after second crypt_load() call
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, default_luks2_pbkdf));
+ OK_(strcmp(pbkdf->hash, default_luks1_hash));
+ EQ_(pbkdf->time_ms, default_luks2_iter_time);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), default_luks2_parallel_threads));
+ crypt_set_iteration_time(cd, 1);
+ OK_(crypt_load(cd, CRYPT_LUKS, NULL));
+ OK_(strcmp(pbkdf->type, default_luks2_pbkdf));
+ OK_(strcmp(pbkdf->hash, default_luks1_hash));
+ EQ_(pbkdf->time_ms, 1);
+ EQ_(pbkdf->max_memory_kb, adjusted_pbkdf_memory());
+ EQ_(pbkdf->parallel_threads, _min(cpus_online(), default_luks2_parallel_threads));
+ CRYPT_FREE(cd);
+
+ // test crypt_set_pbkdf_type() overwrites invalid value set by crypt_set_iteration_time()
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ crypt_set_iteration_time(cd, 0);
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ OK_(strcmp(pbkdf->type, argon2.type));
+ EQ_(pbkdf->time_ms, argon2.time_ms);
+
+ // force iterations
+ argon2.iterations = 33;
+ argon2.flags = CRYPT_PBKDF_NO_BENCHMARK;
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ NOTNULL_(pbkdf = crypt_get_pbkdf_type(cd));
+ EQ_(pbkdf->iterations, 33);
+ EQ_(pbkdf->flags, CRYPT_PBKDF_NO_BENCHMARK);
+
+ // time may be unset with iterations
+ argon2.time_ms = 0;
+ OK_(crypt_set_pbkdf_type(cd, &argon2));
+ argon2.flags &= ~CRYPT_PBKDF_NO_BENCHMARK;
+ FAIL_(crypt_set_pbkdf_type(cd, &argon2), "Illegal time value.");
+
+ pbkdf2.time_ms = 0;
+ pbkdf2.flags = CRYPT_PBKDF_NO_BENCHMARK;
+ pbkdf2.parallel_threads = 0;
+ pbkdf2.max_memory_kb = 0;
+ pbkdf2.iterations = 1000;
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+ pbkdf2.flags &= ~CRYPT_PBKDF_NO_BENCHMARK;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Illegal time value.");
+
+ // hash is relevant only with pbkdf2
+ pbkdf2.time_ms = 9;
+ pbkdf2.hash = NULL;
+ FAIL_(crypt_set_pbkdf_type(cd, &pbkdf2), "Hash is mandatory for pbkdf2");
+ pbkdf2.hash = "sha256";
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf2));
+
+ argon2.time_ms = 9;
+ argon2.hash = "sha256"; // 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_ret_len, key_size = strlen(vk_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 = TST_SECTOR_SIZE
+ };
+
+ crypt_decode_key(key, vk_hex, key_size);
+ crypt_decode_key(key2, vk_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, &params2));
+ 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, &params2));
+ /* 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size_ret, key_size = strlen(vk_hex) / 2, keyslot_key_size = 16;
+ uint64_t r_payload_offset;
+
+ crypt_decode_key(key, vk_hex, key_size);
+ crypt_decode_key(key2, vk_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, &min_pbkdf2));
+ 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, &min_pbkdf2));
+ 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, &min_pbkdf2));
+ 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, &min_pbkdf2));
+ 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));
+ OK_(set_fast_pbkdf(cd));
+ 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, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(prepare_keyfile(KEYFILE2, PASSPHRASE1, strlen(PASSPHRASE1)));
+
+ /* 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_get_label (unrestricted) */
+ NOTNULL_(crypt_get_label(cd));
+ OK_(strcmp("", crypt_get_label(cd)));
+ /* crypt_get_subsystem (unrestricted) */
+ NOTNULL_(crypt_get_subsystem(cd));
+ OK_(strcmp("", crypt_get_subsystem(cd)));
+
+ /* 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, PASSPHRASE, strlen(PASSPHRASE), "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, PASSPHRASE, strlen(PASSPHRASE), "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, PASSPHRASE, strlen(PASSPHRASE)));
+
+ /* crypt_keyslot_add_by_volume_key (restricted) */
+ FAIL_((r = crypt_keyslot_add_by_volume_key(cd, CRYPT_ANY_SLOT, key, key_size, PASSPHRASE1, strlen(PASSPHRASE1))), "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, PASSPHRASE1, strlen(PASSPHRASE1), 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, PASSPHRASE1, strlen(PASSPHRASE1), 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, CRYPT_REQUIREMENT_UNKNOWN);
+
+ /* crypt_activate_by_passphrase (restricted for activation only) */
+ FAIL_((r = crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ OK_(crypt_activate_by_passphrase(cd, NULL, 0, PASSPHRASE, strlen(PASSPHRASE), 0));
+ OK_(crypt_activate_by_passphrase(cd, NULL, 0, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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));
+
+ NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken.");
+ }
+#endif
+
+ /* crypt_volume_key_verify (unrestricted) */
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+
+ /* crypt_get_cipher (unrestricted) */
+ OK_(strcmp(crypt_get_cipher(cd)?:"", "aes"));
+
+ /* crypt_get_cipher_mode (unrestricted) */
+ OK_(strcmp(crypt_get_cipher_mode(cd)?:"", "xts-plain64"));
+
+ /* crypt_get_uuid (unrestricted) */
+ NOTNULL_(crypt_get_uuid(cd));
+
+ /* crypt_get_device_name (unrestricted) */
+ NOTNULL_(crypt_get_device_name(cd));
+
+ /* crypt_get_data_offset (unrestricted) */
+ OK_(!crypt_get_data_offset(cd));
+
+ /* crypt_get_iv_offset (unrestricted, nothing to test) */
+
+ /* crypt_get_volume_key_size (unrestricted) */
+ EQ_(crypt_get_volume_key_size(cd), key_size);
+
+ /* crypt_keyslot_status (unrestricted) */
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE_LAST);
+ EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_INACTIVE);
+
+ /* crypt_keyslot_get_priority (unrestricted) */
+ EQ_(crypt_keyslot_get_priority(cd, 0), CRYPT_SLOT_PRIORITY_NORMAL);
+
+ /* crypt_keyslot_set_priority (restricted) */
+ FAIL_((r = crypt_keyslot_set_priority(cd, 0, CRYPT_SLOT_PRIORITY_PREFER)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_keyslot_area (unrestricted) */
+ OK_(crypt_keyslot_area(cd, 0, &dummy, &dummy));
+ OK_(!dummy);
+
+ /* crypt_header_backup (unrestricted) */
+ remove(BACKUP_FILE);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS, BACKUP_FILE));
+
+ /* crypt_header_restore (restricted, do not drop the test until we have safe option) */
+ FAIL_((r = crypt_header_restore(cd, CRYPT_LUKS2, BACKUP_FILE)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+ remove(BACKUP_FILE);
+
+ /* crypt_token_json_set (restricted) */
+ FAIL_((r = crypt_token_json_set(cd, CRYPT_ANY_TOKEN, json)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_json_get (unrestricted) */
+ OK_(crypt_token_json_get(cd, 0, &token));
+ NOTNULL_(strstr(token, "user_type"));
+
+ /* crypt_token_status (unrestricted) */
+ EQ_(crypt_token_status(cd, 0, &token), CRYPT_TOKEN_EXTERNAL_UNKNOWN);
+ OK_(strcmp(token, "user_type"));
+ EQ_(crypt_token_status(cd, 1, &token), CRYPT_TOKEN_INTERNAL);
+ OK_(strcmp(token, "luks2-keyring"));
+ EQ_(crypt_token_status(cd, 2, NULL), CRYPT_TOKEN_INACTIVE);
+ EQ_(crypt_token_status(cd, 6, &token), CRYPT_TOKEN_INTERNAL_UNKNOWN);
+
+ /* crypt_token_luks2_keyring_set (restricted) */
+ FAIL_((r = crypt_token_luks2_keyring_set(cd, CRYPT_ANY_TOKEN, &params)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_luks2_keyring_get (unrestricted) */
+ EQ_(crypt_token_luks2_keyring_get(cd, 1, &params_get), 1);
+ OK_(strcmp(params_get.key_description, KEY_DESC_TEST0));
+
+ /* crypt_token_assign_keyslot (unrestricted) */
+ FAIL_((r = crypt_token_assign_keyslot(cd, 0, 1)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_token_unassign_keyslot (unrestricted) */
+ FAIL_((r = crypt_token_unassign_keyslot(cd, CRYPT_ANY_TOKEN, CRYPT_ANY_SLOT)), "Unmet requirements detected");
+ EQ_(r, -ETXTBSY);
+
+ /* crypt_activate_by_token (restricted for activation only) */
+#ifdef KERNEL_KEYRING
+ if (t_dm_crypt_keyring_support()) {
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ NOTFAIL_(kid, "Test or kernel keyring are broken.");
+
+ 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));
+
+ NOTFAIL_(keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING), "Test or kernel keyring are broken.");
+ }
+#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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE))), "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, PASSPHRASE, strlen(PASSPHRASE)));
+ 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, PASSPHRASE, strlen(PASSPHRASE), 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 *cipher = "aes";
+ const char *cipher_mode = "xts-random";
+ int ret;
+
+ // FIXME: This is just a stub
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size, &params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, NULL, key_size, PASSPHRASE, strlen(PASSPHRASE)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_2, 7, PASSPHRASE, strlen(PASSPHRASE) ,0), 7);
+ 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, &params), "Wrong key size.");
+ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, "xts-plainx", NULL, NULL, key_size, &params), "Wrong cipher.");
+ CRYPT_FREE(cd);
+}
+
+static int 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c645be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb22158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(vk_hex) / 2;
+ struct crypt_params_luks2 params = {
+ .sector_size = 512,
+ .integrity = "aead"
+ };
+ struct crypt_active_device cad = {};
+
+ crypt_decode_key(key, vk_hex, key_size);
+ crypt_decode_key(key1, vk_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, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 0));
+
+ /* check we can refresh significant flags */
+ if (t_dm_crypt_discard_support()) {
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), CRYPT_ACTIVATE_READONLY));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 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, &params)) {
+ OK_(crypt_keyslot_add_by_volume_key(cd2, 0, key, 32, PASSPHRASE, strlen(PASSPHRASE)));
+ 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE)));
+ FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 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, PASSPHRASE, strlen(PASSPHRASE)));
+ FAIL_(crypt_activate_by_passphrase(cd2, CDEVICE_1, 0, PASSPHRASE, strlen(PASSPHRASE), 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_C(0);
+ OK_(crypt_persistent_flags_get(cd, CRYPT_FLAGS_ACTIVATION, &flags));
+ EQ_(flags,CRYPT_ACTIVATE_ALLOW_DISCARDS | CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS);
+
+ /* label and subsystem (second label */
+ OK_(crypt_set_label(cd, "label", "subsystem"));
+ OK_(strcmp("label", crypt_get_label(cd)));
+ OK_(strcmp("subsystem", crypt_get_subsystem(cd)));
+
+ OK_(crypt_set_label(cd, NULL, NULL));
+ OK_(strcmp("", crypt_get_label(cd)));
+ OK_(strcmp("", crypt_get_subsystem(cd)));
+
+ CRYPT_FREE(cd);
+}
+
+#if KERNEL_KEYRING && USE_LUKS2_REENCRYPTION
+static int test_progress(uint64_t size __attribute__((unused)),
+ uint64_t offset __attribute__((unused)),
+ void *usrptr __attribute__((unused)))
+{
+ 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 = "sha256",
+ .luks2 = &params2,
+ };
+ dev_t devno;
+
+ const char *vk_hex = "bb21babe733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_hex) / 2;
+ char key[128];
+
+ crypt_decode_key(key, vk_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, &params2));
+ 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_run(cd, NULL, 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_run(cd, NULL, 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_run(cd, NULL, 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_run(cd, NULL, 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 = "sha256";
+ OK_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 21, 9, "aes", "xts-plain64", &rparams));
+ OK_(crypt_reencrypt_run(cd, NULL, 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_run(cd2, NULL, 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_run(cd, NULL, 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, &params2));
+ 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 = 2;
+ OK_(crypt_reencrypt_run(cd, &test_progress, NULL));
+ 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_run(cd, NULL, 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, &params2));
+ OK_(crypt_format(cd2, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, &params2));
+ 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, &params2));
+ 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 = &params2;
+ 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_run(cd, NULL, 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, &params2));
+ 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_run(cd, NULL, 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, &params2));
+ 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_run(cd, NULL, 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 = &params2;
+ 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, &params2));
+ 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_run(cd, NULL, NULL));
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, 8*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, &params2));
+ 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_run(cd, NULL, NULL));
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, 2*8200));
+
+ 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 = &params2;
+ 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, &params2));
+ 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, &params2));
+ 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_run(cd, NULL, 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, &params2));
+ 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_run(cd, NULL, NULL));
+ CRYPT_FREE(cd);
+
+ /* decryption forward (online) */
+ 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, &params2));
+ OK_(crypt_set_pbkdf_type(cd, &pbkdf));
+ 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);
+ 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, CDEVICE_2, PASSPHRASE, strlen(PASSPHRASE), 6, CRYPT_ANY_SLOT, NULL, NULL, &rparams));
+ OK_(crypt_reencrypt_run(cd, NULL, 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, &params2));
+ 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_run(cd, NULL, 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, &params2));
+ 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);
+ // store devno for later size check
+ OK_(t_get_devno(CDEVICE_2, &devno));
+ // 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_run(cd, NULL, NULL));
+ remove(BACKUP_FILE);
+ OK_(t_device_size_by_devno(devno, &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, &params2));
+ 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 = &params2;
+
+ OK_(crypt_reencrypt_init_by_passphrase(cd, CDEVICE_1, PASSPHRASE, strlen(PASSPHRASE), 6, 1, "aes", "cbc-essiv:sha256", &rparams));
+ OK_(crypt_reencrypt_run(cd, NULL, 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, &params2));
+ 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 sectors of device */
+ test_progress_steps = 2;
+ OK_(crypt_reencrypt_run(cd, &test_progress, NULL));
+
+ /* 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 = 2;
+ OK_(crypt_reencrypt_run(cd, &test_progress, NULL));
+
+ /* 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, &params2));
+ 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 = &params2;
+ 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 = 2;
+ OK_(crypt_reencrypt_run(cd, &test_progress, NULL));
+ 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_run(cd, NULL, 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 = &params2;
+ 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, &params2));
+ 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_run(cd, NULL, 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();
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, 2 * r_header_size));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+
+ rparams = (struct crypt_params_reencrypt) {
+ .mode = CRYPT_REENCRYPT_DECRYPT,
+ .direction = CRYPT_REENCRYPT_FORWARD,
+ .resilience = "datashift-checksum",
+ .hash = "sha256",
+ .data_shift = r_header_size,
+ .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT
+ };
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, NULL));
+ EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE));
+ CRYPT_FREE(cd);
+
+ params2.data_device = DMDIR L_DEVICE_OK;
+ params2.sector_size = 512;
+
+ /* create detached LUKS2 header (with data_offset == 0) */
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, &params2));
+ EQ_(crypt_get_data_offset(cd), 0);
+ OK_(set_fast_pbkdf(cd));
+ EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)));
+ CRYPT_FREE(cd);
+
+ /* initiate LUKS2 decryption with datashift on bogus LUKS2 header (data_offset == 0) */
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Illegal data offset");
+ /* reencryption must not initialize */
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+ CRYPT_FREE(cd);
+ /* original data device must stay untouched */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+ CRYPT_FREE(cd);
+
+ 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));
+
+ /* simulate read error at first segment beyond data offset*/
+ OK_(dmdevice_error_io(L_DEVICE_OK, DMDIR L_DEVICE_OK, DEVICE_ERROR, 0, r_header_size, 8, ERR_RD));
+
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Could not read first data segment");
+ CRYPT_FREE(cd);
+
+ /* Device must not be in reencryption */
+ OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ /* simulate write error in original LUKS2 header area */
+ OK_(dmdevice_error_io(L_DEVICE_OK, DMDIR L_DEVICE_OK, DEVICE_ERROR, 0, 0, 8, ERR_WR));
+
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Could not write first data segment");
+ CRYPT_FREE(cd);
+
+ /* Device must not be in reencryption */
+ OK_(crypt_init_data_device(&cd, BACKUP_FILE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+ CRYPT_FREE(cd);
+ remove(BACKUP_FILE);
+
+ /* remove error mapping */
+ OK_(dmdevice_error_io(L_DEVICE_OK, DMDIR L_DEVICE_OK, DEVICE_ERROR, 0, 0, 8, ERR_REMOVE));
+
+ /* test various bogus reencryption resilience parameters */
+ rparams = (struct crypt_params_reencrypt) {
+ .mode = CRYPT_REENCRYPT_DECRYPT,
+ .direction = CRYPT_REENCRYPT_FORWARD,
+ .resilience = "checksum", /* should have been datashift-checksum */
+ .hash = "sha256",
+ .data_shift = r_header_size,
+ .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT
+ };
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, NULL));
+ EQ_(0, crypt_keyslot_add_by_volume_key(cd, 0, NULL, 64, PASSPHRASE, strlen(PASSPHRASE)));
+ OK_(crypt_header_backup(cd, CRYPT_LUKS2, BACKUP_FILE));
+ CRYPT_FREE(cd);
+
+ 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));
+
+ /* decryption on device with data offset and no datashift subvariant mode */
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ rparams.resilience = "journal"; /* should have been datashift-journal */
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ rparams = (struct crypt_params_reencrypt) {
+ .mode = CRYPT_REENCRYPT_DECRYPT,
+ .direction = CRYPT_REENCRYPT_FORWARD,
+ .resilience = "datashift-checksum",
+ .hash = "sha256",
+ .data_shift = 0, /* must be non zero */
+ .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY | CRYPT_REENCRYPT_MOVE_FIRST_SEGMENT
+ };
+
+ /* datashift = 0 */
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ rparams.resilience = "datashift-journal";
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ rparams.resilience = "datashift"; /* datashift only is not supported in decryption mode with moved segment */
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, "aes", "cbc-essiv:sha256", NULL, NULL, 32, &params2));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 21, NULL, 32, PASSPHRASE, strlen(PASSPHRASE)), 21);
+
+ rparams = (struct crypt_params_reencrypt) {
+ .mode = CRYPT_REENCRYPT_REENCRYPT,
+ .direction = CRYPT_REENCRYPT_FORWARD,
+ .resilience = "datashift-checksum",
+ .hash = "sha256",
+ .data_shift = r_header_size,
+ .flags = CRYPT_REENCRYPT_INITIALIZE_ONLY
+ };
+
+ /* regular reencryption must not accept datashift subvariants */
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ rparams.resilience = "datashift-journal";
+ FAIL_(crypt_reencrypt_init_by_passphrase(cd, NULL, PASSPHRASE, strlen(PASSPHRASE), 0, CRYPT_ANY_SLOT, NULL, NULL, &rparams), "Invalid reencryption params");
+ EQ_(crypt_reencrypt_status(cd, NULL), CRYPT_REENCRYPT_NONE);
+
+ CRYPT_FREE(cd);
+ _cleanup_dmdevices();
+}
+#endif
+
+static void LuksKeyslotAdd(void)
+{
+ struct crypt_params_luks2 params = {
+ .sector_size = 512
+ };
+ char key[128], key3[128];
+#ifdef KERNEL_KEYRING
+ int ks;
+ key_serial_t kid;
+#endif
+ const struct crypt_token_params_luks2_keyring tparams = {
+ .key_description = KEY_DESC_TEST0
+ };
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+ struct crypt_keyslot_context *um1, *um2;
+
+ crypt_decode_key(key, vk_hex, key_size);
+ crypt_decode_key(key3, vk_hex2, key_size);
+
+ // init test devices
+ OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
+
+ // test support for embedded key (after crypt_format)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, NULL, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
+ EQ_(crypt_keyslot_status(cd, 3), CRYPT_SLOT_ACTIVE_LAST);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+ CRYPT_FREE(cd);
+
+ // test add by volume key
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE1, strlen(PASSPHRASE1), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, CRYPT_ANY_SLOT, um2, 0), 0);
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_ACTIVE);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // Add by same passphrase
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um1, 0), 1);
+ EQ_(crypt_keyslot_status(cd, 1), CRYPT_SLOT_ACTIVE);
+ crypt_keyslot_context_free(um1);
+
+ // new passphrase can't be provided by key method
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, 1, um1, CRYPT_ANY_SLOT, um2, 0), "Can't get passphrase via selected unlock method");
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // add by keyfile
+ OK_(prepare_keyfile(KEYFILE1, PASSPHRASE1, strlen(PASSPHRASE1)));
+ OK_(prepare_keyfile(KEYFILE2, KEY1, strlen(KEY1)));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, 0, um1, 2, um2, 0), 2);
+ EQ_(crypt_keyslot_status(cd, 2), CRYPT_SLOT_ACTIVE);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // add by same keyfile
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um1));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, 2, um1, 4, um1, 0), 4);
+ EQ_(crypt_keyslot_status(cd, 4), CRYPT_SLOT_ACTIVE);
+ crypt_keyslot_context_free(um1);
+
+ // keyslot already exists
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, 3, um1, 0, um2, 0), "Keyslot already exists.");
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // generate new unbound key
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, NULL, 9, &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 10, um2, CRYPT_VOLUME_KEY_NO_SEGMENT), 10);
+ EQ_(crypt_keyslot_status(cd, 10), CRYPT_SLOT_UNBOUND);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ EQ_(crypt_token_luks2_keyring_set(cd, 3, &tparams), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 3, 1), 3);
+ EQ_(crypt_token_assign_keyslot(cd, 3, 3), 3);
+
+ // test unlocking/adding keyslot by LUKS2 token
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ // passphrase not in keyring
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 13, um2, 0), "No token available.");
+#ifdef KERNEL_KEYRING
+ // wrong passphrase in keyring
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
+ NOTFAIL_(kid, "Test or kernel keyring are broken.");
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 13, um2, 0), "No token available.");
+
+ // token unlocks keyslot
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE, strlen(PASSPHRASE), KEY_SPEC_THREAD_KEYRING);
+ NOTFAIL_(kid, "Test or kernel keyring are broken.");
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 13, um2, 0), 13);
+ EQ_(crypt_keyslot_status(cd, 13), CRYPT_SLOT_ACTIVE);
+
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // token provides passphrase for new keyslot
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, 3, um1, 30, um2, 0), 30);
+ EQ_(crypt_keyslot_status(cd, 30), CRYPT_SLOT_ACTIVE);
+ OK_(crypt_token_is_assigned(cd, 3, 30));
+
+ // unlock and add by same token
+ crypt_keyslot_context_free(um1);
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
+ ks = crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, CRYPT_ANY_SLOT, um1, 0);
+ GE_(ks, 0);
+ EQ_(crypt_keyslot_status(cd, ks), CRYPT_SLOT_ACTIVE);
+ OK_(crypt_token_is_assigned(cd, 3, ks));
+#endif
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void VolumeKeyGet(void)
+{
+ struct crypt_params_luks2 params = {
+ .sector_size = 512
+ };
+ char key[256], key2[256];
+#ifdef KERNEL_KEYRING
+ key_serial_t kid;
+ const struct crypt_token_params_luks2_keyring tparams = {
+ .key_description = KEY_DESC_TEST0
+ };
+#endif
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"
+ "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1b";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "xts-plain64";
+ uint64_t r_payload_offset;
+ struct crypt_keyslot_context *um1, *um2;
+
+ crypt_decode_key(key, vk_hex, key_size);
+
+ OK_(prepare_keyfile(KEYFILE1, PASSPHRASE1, strlen(PASSPHRASE1)));
+
+#ifdef KERNEL_KEYRING
+ kid = add_key("user", KEY_DESC_TEST0, PASSPHRASE1, strlen(PASSPHRASE1), KEY_SPEC_THREAD_KEYRING);
+ NOTFAIL_(kid, "Test or kernel keyring are broken.");
+#endif
+
+ // init test devices
+ OK_(get_luks2_offsets(0, 0, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
+
+ // test support for embedded key (after crypt_format)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ key_size--;
+ FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL), "buffer too small");
+
+ // check cached generated volume key can be retrieved
+ key_size++;
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
+ OK_(crypt_volume_key_verify(cd, key2, key_size));
+ CRYPT_FREE(cd);
+
+ // check we can add keyslot via retrieved key
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+ CRYPT_FREE(cd);
+
+ // check selected volume key can be retrieved and added
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(set_fast_pbkdf(cd));
+ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, &params));
+ memset(key2, 0, key_size);
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
+ OK_(memcmp(key, key2, key_size));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 0, um2, 0), 0);
+ crypt_keyslot_context_free(um2);
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um2, 0), 1);
+ crypt_keyslot_context_free(um2);
+#ifdef KERNEL_KEYRING
+ EQ_(crypt_token_luks2_keyring_set(cd, 0, &tparams), 0);
+ EQ_(crypt_token_assign_keyslot(cd, 0, 1), 0);
+#endif
+ crypt_keyslot_context_free(um1);
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS2, NULL));
+ // check key context is not usable
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
+ crypt_keyslot_context_free(um1);
+
+ // by passphrase
+ memset(key2, 0, key_size);
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 0);
+ OK_(memcmp(key, key2, key_size));
+ memset(key2, 0, key_size);
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, 0, key2, &key_size, um1), 0);
+ OK_(memcmp(key, key2, key_size));
+ crypt_keyslot_context_free(um1);
+
+ // by keyfile
+ memset(key2, 0, key_size);
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
+ OK_(memcmp(key, key2, key_size));
+ memset(key2, 0, key_size);
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, 1, key2, &key_size, um1), 1);
+ crypt_keyslot_context_free(um1);
+
+#ifdef KERNEL_KEYRING
+ // by token
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
+ memset(key2, 0, key_size);
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
+ OK_(memcmp(key, key2, key_size));
+ crypt_keyslot_context_free(um1);
+#endif
+ CRYPT_FREE(cd);
+
+ _remove_keyfiles();
+ _cleanup_dmdevices();
+}
+
+static int _crypt_load_check(struct crypt_device *cd)
+{
+#ifdef HAVE_BLKID
+ return crypt_load(cd, CRYPT_LUKS, NULL);
+#else
+ return -ENOTSUP;
+#endif
+}
+
+static void Luks2Repair(void)
+{
+ char rollback[256];
+
+ GE_(snprintf(rollback, sizeof(rollback),
+ "dd if=" IMAGE_PV_LUKS2_SEC ".bcp of=%s bs=1M 2>/dev/null", DEVICE_6), 0);
+
+ OK_(crypt_init(&cd, DEVICE_6));
+
+ FAIL_(_crypt_load_check(cd), "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_check(cd), "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_check(cd), "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_check(cd), "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_(LuksKeyslotAdd, "Adding keyslot via new API");
+ RUN_(VolumeKeyGet, "Getting volume key via keyslot context API");
+ 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..aa430dd
--- /dev/null
+++ b/tests/api-test.c
@@ -0,0 +1,2411 @@
+/*
+ * cryptsetup library API check functions
+ *
+ * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2016-2023 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 "luks1/luks.h"
+#include "libcryptsetup.h"
+
+#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 "blablabl"
+#define PASSPHRASE1 "albalbal"
+
+#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, TST_SECTOR_SIZE);
+ current_sector = DIV_ROUND_UP_MODULO(DIV_ROUND_UP(LUKS_PHDR_SIZE_B, TST_SECTOR_SIZE),
+ LUKS_ALIGN_KEYSLOTS / TST_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 / TST_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 / TST_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 (!test_loop_file)
+ return 1;
+
+ if ((fd=mkstemp(test_loop_file)) == -1) {
+ printf("cannot create temporary file with template %s\n", test_loop_file);
+ return 1;
+ }
+ close(fd);
+ if (snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ test_loop_file, TST_SECTOR_SIZE, TST_LOOP_FILE_SIZE) < 0)
+ return 1;
+ 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 (!tmp_file_1)
+ return 1;
+
+ if ((fd=mkstemp(tmp_file_1)) == -1) {
+ printf("cannot create temporary file with template %s\n", tmp_file_1);
+ return 1;
+ }
+ close(fd);
+ if (snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null",
+ tmp_file_1, TST_SECTOR_SIZE, 10) < 0)
+ return 1;
+ 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 >/dev/null 2>&1", 0);
+ _system("modprobe dm-verity >/dev/null 2>&1", 0);
+ _system("modprobe dm-integrity >/dev/null 2>&1", 0);
+
+ if (t_dm_check_versions())
+ return 1;
+
+ _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 = "sha256",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ int fd;
+ char key[128], key2[128], path[128];
+ struct crypt_keyslot_context *kc = NULL;
+
+ const char *passphrase = "blabla";
+ // hashed hex version of PASSPHRASE
+ const char *vk_hex = "ccadd99b16cd3d200c22d6db45d8b6630ef3d936767127347ec8a76ab992c2ea";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+
+ uint64_t size, r_size;
+
+ crypt_decode_key(key, vk_hex, key_size);
+ FAIL_(crypt_init(&cd, ""), "empty device string");
+ FAIL_(crypt_init(&cd, DEVICE_WRONG), "nonexistent device name ");
+ FAIL_(crypt_init(&cd, DEVICE_CHAR), "character device as backing device");
+ OK_(crypt_init(&cd, tmp_file_1));
+ CRYPT_FREE(cd);
+
+ // test crypt_format, crypt_get_cipher, crypt_get_cipher_mode, crypt_get_volume_key_size
+ OK_(crypt_init(&cd,DEVICE_1));
+ params.skip = 3;
+ params.offset = 42;
+ FAIL_(crypt_format(cd,CRYPT_PLAIN,NULL,cipher_mode,NULL,NULL,key_size,&params),"cipher param is null");
+ FAIL_(crypt_format(cd,CRYPT_PLAIN,cipher,NULL,NULL,NULL,key_size,&params),"cipher_mode param is null");
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd)));
+ OK_(strcmp(cipher,crypt_get_cipher(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(params.skip, crypt_get_iv_offset(cd));
+ EQ_(params.offset, crypt_get_data_offset(cd));
+ params.skip = 0;
+ params.offset = 0;
+
+ // crypt_set_uuid()
+ FAIL_(crypt_set_uuid(cd,DEVICE_1_UUID),"can't set uuid to plain device");
+
+ CRYPT_FREE(cd);
+
+ // default is "plain" hash - no password hash
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, NULL));
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0), "cannot verify key with plain");
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ 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 >> TST_SECTOR_SHIFT;
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ EQ_(crypt_get_data_offset(cd),params.offset);
+ // device size is 0 sectors
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0), "invalid device size (0 blocks)");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // data part of crypt device is of 1 sector size
+ params.offset = (size >> TST_SECTOR_SHIFT) - 1;
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ GE_(snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1), 0);
+ if (t_device_size(path, &r_size) >= 0)
+ EQ_(r_size >> TST_SECTOR_SHIFT, 1);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // size > device_size
+ params.offset = 0;
+ params.size = (size >> TST_SECTOR_SHIFT) + 1;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // offset == device_size (autodetect size)
+ params.offset = (size >> TST_SECTOR_SHIFT);
+ params.size = 0;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // offset == device_size (user defined size)
+ params.offset = (size >> TST_SECTOR_SHIFT);
+ params.size = 123;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // offset+size > device_size
+ params.offset = 42;
+ params.size = (size >> TST_SECTOR_SHIFT) - params.offset + 1;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Offset and size are beyond device real size");
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // offset+size == device_size
+ params.offset = 42;
+ params.size = (size >> TST_SECTOR_SHIFT) - params.offset;
+ crypt_init(&cd, DEVICE_1);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ if (!t_device_size(path, &r_size))
+ EQ_((r_size >> TST_SECTOR_SHIFT),params.size);
+ OK_(crypt_deactivate(cd,CDEVICE_1));
+
+ CRYPT_FREE(cd);
+ params.hash = "sha256";
+ params.offset = 0;
+ params.size = 0;
+ params.skip = 0;
+
+ // Now use hashed password
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ FAIL_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),
+ "cannot verify passphrase with plain" );
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+
+ // device status check
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ GE_(snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1), 0);
+ fd = open(path, O_RDONLY);
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_BUSY);
+ FAIL_(crypt_deactivate(cd, CDEVICE_1), "Device is busy");
+ close(fd);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ CRYPT_FREE(cd);
+
+ // crypt_init_by_name_and_header
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ CRYPT_FREE(cd);
+
+ // init with detached header is not supported
+ OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_1));
+ FAIL_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params),
+ "can't use plain with separate metadata device");
+ CRYPT_FREE(cd);
+
+ FAIL_(crypt_init_by_name_and_header(&cd, CDEVICE_1, H_DEVICE),"can't init plain device by header device");
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd)));
+ OK_(strcmp(cipher,crypt_get_cipher(cd)));
+ EQ_((int)key_size, crypt_get_volume_key_size(cd));
+ EQ_(params.skip, crypt_get_iv_offset(cd));
+ EQ_(params.offset, crypt_get_data_offset(cd));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ params.size = 0;
+ params.offset = 0;
+
+ // crypt_set_data_device
+ FAIL_(crypt_set_data_device(cd,H_DEVICE),"can't set data device for plain device");
+ NULL_(crypt_get_metadata_device_name(cd));
+ FAIL_(crypt_header_is_detached(cd), "plain has no header");
+
+ // 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 >> TST_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 >> TST_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 >> TST_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 >> TST_SECTOR_SHIFT) - params.offset - 10;
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(crypt_activate_by_volume_key(cd,CDEVICE_1,key,key_size,0));
+ if (!t_device_size(path,&r_size))
+ EQ_(r_size >> TST_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 >> TST_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 >> TST_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 >> TST_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 >> TST_SECTOR_SHIFT, (size >> TST_SECTOR_SHIFT)- 42);
+ OK_(crypt_deactivate(cd,CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ params.size = 0;
+ params.offset = 0;
+ OK_(crypt_init(&cd,DEVICE_1));
+ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,&params));
+ OK_(crypt_activate_by_volume_key(cd,CDEVICE_1,key,key_size,0));
+
+ // suspend/resume tests
+ FAIL_(crypt_suspend(cd,CDEVICE_1),"cannot suspend plain device");
+ 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));
+ memset(key2, 0, key_size);
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase, strlen(passphrase), &kc));
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, kc));
+ OK_(memcmp(key, key2, key_size));
+ crypt_keyslot_context_free(kc);
+
+ 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,&params));
+
+ // crypt_keyslot_*()
+ FAIL_(crypt_keyslot_add_by_passphrase(cd,CRYPT_ANY_SLOT,passphrase,strlen(passphrase),passphrase,strlen(passphrase)), "can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_add_by_volume_key(cd,CRYPT_ANY_SLOT ,key,key_size,passphrase,strlen(passphrase)),"can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_add_by_keyfile(cd,CRYPT_ANY_SLOT,KEYFILE1,strlen(KEY1),KEYFILE2,strlen(KEY2)),"can't add keyslot to plain device");
+ FAIL_(crypt_keyslot_destroy(cd,1),"can't manipulate keyslots on plain device");
+ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_INVALID);
+ FAIL_(crypt_set_label(cd, "label", "subsystem"), "can't set labels for plain device");
+ NULL_(crypt_get_label(cd));
+ NULL_(crypt_get_subsystem(cd));
+ _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 = "sha256",
+ .skip = 0,
+ .offset = 0,
+ };
+
+ size_t key_size = 256 / 8;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ const char *passphrase = PASSPHRASE;
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ new_messages = 0;
+ crypt_set_log_callback(cd, &new_log, NULL);
+ EQ_(new_messages, 0);
+ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, &params));
+ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0));
+ 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);
+
+ /* skip tests using empty passphrase */
+ if(_fips_mode)
+ return;
+
+ 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 = PASSPHRASE, *passphrase2 = "nsdkFI&Y#.sd";
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(vk_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, vk_hex, key_size);
+ crypt_decode_key(key3, vk_hex2, key_size);
+
+ // init test devices
+ OK_(get_luks_offsets(1, key_size, 0, 0, &r_header_size, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1));
+
+ // format
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ params.data_alignment = 0;
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Not enough space for keyslots material");
+ CRYPT_FREE(cd);
+
+ // test payload_offset = 0 for encrypted device with external header device
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), 0);
+ CRYPT_FREE(cd);
+
+ params.data_alignment = 0;
+ params.data_device = NULL;
+
+ // test payload_offset = 0. format() should look up alignment offset from device topology
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(!(crypt_get_data_offset(cd) > 0));
+ CRYPT_FREE(cd);
+
+ // set_data_offset has priority, alignment must be 0 or must be compatible
+ params.data_alignment = 0;
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_8M));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ CRYPT_FREE(cd);
+
+ // Load gets the value from metadata
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ EQ_(crypt_get_data_offset(cd), OFFSET_8M);
+ CRYPT_FREE(cd);
+
+ params.data_alignment = OFFSET_4M;
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_set_data_offset(cd, OFFSET_2M + 1), "Not aligned to 4096"); // must be aligned to 4k
+ OK_(crypt_set_data_offset(cd, OFFSET_2M));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Alignment not compatible");
+ OK_(crypt_set_data_offset(cd, OFFSET_4M));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), OFFSET_4M);
+ CRYPT_FREE(cd);
+
+ /*
+ * test limit values for backing device size
+ */
+ params.data_alignment = OFFSET_2M;
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ //OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset - 1));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, 2050 - 1)); //FIXME last keyslot - 1 sector
+
+ // 1 sector less than required
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_WRONG));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Device too small");
+ CRYPT_FREE(cd);
+
+ // 0 sectors for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small");
+ CRYPT_FREE(cd);
+
+ // 1 sector for encrypted area
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_get_data_offset(cd), params.data_alignment);
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1));
+ EQ_(r_size_1, TST_SECTOR_SIZE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE);
+ // restrict format only to empty context
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted");
+ // change data device to wrong one
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S));
+ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small");
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+
+ // generate keyslot material at the end of luks header
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7);
+ CRYPT_FREE(cd);
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params), "Context is already formatted");
+ 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, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(0, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+ params.data_alignment = 0;
+ params.data_device = DEVICE_2;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ // there we've got uuid mismatch
+ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE));
+ 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));
+ EQ_(crypt_header_is_detached(cd), 1);
+ CRYPT_FREE(cd);
+
+ params.data_device = NULL;
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+
+ // even with no keyslots defined it can be activated by volume key
+ OK_(crypt_volume_key_verify(cd, key, key_size));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0));
+ 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_dump_json(cd, NULL, 0), "LUKS1 not supported");
+
+ 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_set_label(cd, "label", "subsystem"), "can't set labels for LUKS1 device");
+ NULL_(crypt_get_label(cd));
+ NULL_(crypt_get_subsystem(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, &params));
+ 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
+ GE_(snprintf(tmp, sizeof(tmp), "dmsetup create %s --table \""
+ "0 100 crypt aes-cbc-essiv:sha256 deadbabedeadbabedeadbabedeadbabe 0 "
+ "%s 2048\"", CDEVICE_2, DEVICE_2), 0);
+ _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)
+ GE_(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), 0);
+ _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 = "sha256",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], key2[128], cmd[256];
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+
+ crypt_decode_key(key, vk_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, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_1), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_2), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_3), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_4), "Header too small");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_5), "Header corrupted");
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ // wipe valid luks header
+ GE_(snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=" DMDIR L_DEVICE_OK " bs=512 count=%" PRIu64 " 2>/dev/null", r_payload_offset), 0);
+ OK_(_system(cmd, 1));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_1), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_2), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_3), "Header corrupted");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_4), "Header too small");
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, EVL_HEADER_5), "Header corrupted");
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // volume key_size mismatch
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ memcpy(key2, key, key_size / 2);
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key2, key_size / 2, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER), "Volume keysize mismatch");
+ CRYPT_FREE(cd);
+
+ // payload offset mismatch
+ params.data_alignment = 8192;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER), "Payload offset mismatch");
+ //_system("dmsetup table;sleep 1",1);
+ CRYPT_FREE(cd);
+
+ /* check crypt_header_restore() properly loads crypt_device context */
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+ /* same test, any LUKS */
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 1*1024*1024, 1*1024*1024, 0, NULL, NULL));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS, VALID_HEADER));
+ OK_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0));
+
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void LuksHeaderLoad(void)
+{
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048,
+ };
+ struct crypt_params_plain pl_params = {
+ .hash = "sha256",
+ .skip = 0,
+ .offset = 0,
+ .size = 0
+ };
+ char key[128], cmd[256];
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_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
+ //GE_(snprintf(cmd, sizeof(cmd), "dd if=" EVL_HEADER_4 " of=" DMDIR H_DEVICE_WRONG " bs=512 count=%" PRIu64, r_payload_offset - 1), 0);
+ GE_(snprintf(cmd, sizeof(cmd), "dd if=" EVL_HEADER_4 " of=" DMDIR H_DEVICE_WRONG " bs=512 count=%d 2>/dev/null", 2050 - 1), 0);
+ OK_(_system(cmd, 1));
+ // some device
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ // 1 sector device
+ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1));
+ // 0 sectors device for payload
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset));
+
+ // valid metadata and device size
+ params.data_alignment = 0;
+ params.data_device = DMDIR L_DEVICE_OK;
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ 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));
+ EQ_(1, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+
+ // repeat with init with two devices
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ CRYPT_FREE(cd);
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DMDIR L_DEVICE_OK));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(!crypt_get_metadata_device_name(cd));
+ EQ_(strcmp(DMDIR H_DEVICE, crypt_get_metadata_device_name(cd)), 0);
+ EQ_(1, crypt_header_is_detached(cd));
+ CRYPT_FREE(cd);
+
+ // bad header: device too small (payloadOffset > device_size)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG));
+ FAIL_(crypt_load(cd, CRYPT_LUKS1, NULL), "Device too small");
+ NULL_(crypt_get_type(cd));
+ CRYPT_FREE(cd);
+
+ // 0 secs for encrypted data area
+ params.data_alignment = 2048;
+ params.data_device = NULL;
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ FAIL_(crypt_set_metadata_size(cd, 0x004000, 0x004000), "Wrong context type");
+ OK_(crypt_get_metadata_size(cd, &mdata_size, &keyslots_size));
+ EQ_(mdata_size, LUKS_ALIGN_KEYSLOTS);
+ EQ_(keyslots_size, r_header_size * TST_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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_hex, key_size);
+
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1));
+
+ // create LUKS device and backup the header
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7);
+ EQ_(crypt_keyslot_add_by_volume_key(cd, 0, key, key_size, passphrase, strlen(passphrase)), 0);
+ OK_(crypt_header_backup(cd, CRYPT_LUKS1, BACKUP_FILE));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // restore header from backup
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_header_restore(cd, CRYPT_LUKS1, BACKUP_FILE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ EQ_(0, crypt_header_is_detached(cd));
+ 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));
+ EQ_(1, crypt_header_is_detached(cd));
+ 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 *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_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, vk_hex, key_size);
+
+ // prepare env
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(get_luks_offsets(1, key_size, 0, 0, &r_header_size, NULL));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size));
+ OK_(create_dmdevice_over_loop(L_DEVICE_OK, r_payload_offset + 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_0S, 1000));
+ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset + 1000));
+
+ // test header and encrypted payload all in one device
+ OK_(crypt_init(&cd, DMDIR L_DEVICE_OK));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 42));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(42, r_size >> TST_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 >> TST_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 >> TST_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, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0));
+ OK_(crypt_resize(cd, CDEVICE_1, 666));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(666, r_size >> TST_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 >> TST_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 >> TST_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, &params));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ CRYPT_FREE(cd2);
+
+ OK_(crypt_init(&cd2, DMDIR L_DEVICE_WRONG));
+ OK_(crypt_format(cd2, CRYPT_PLAIN, cipher, cipher_mode, NULL, key, key_size, NULL));
+ OK_(crypt_activate_by_volume_key(cd2, CDEVICE_2, key, key_size, 0));
+ FAIL_(crypt_resize(cd2, CDEVICE_1, 1), "Device got resized by wrong device context.");
+ OK_(crypt_deactivate(cd2, CDEVICE_2));
+ CRYPT_FREE(cd2);
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void HashDevicePlain(void)
+{
+ struct crypt_params_plain params = {
+ .hash = NULL,
+ .skip = 0,
+ .offset = 0,
+ };
+
+ size_t key_size;
+ const char *vk_hex, *keystr;
+ char key[256];
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+
+ // hash PLAIN, short key
+ OK_(prepare_keyfile(KEYFILE1, "tooshort", 8));
+ FAIL_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 16, 0), "not enough data in keyfile");
+ _remove_keyfiles();
+
+ // hash PLAIN, exact key
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ vk_hex = "caffeecaffeecaffeecaffeecaffee88";
+ key_size = 16;
+ crypt_decode_key(key, vk_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, vk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+
+ // Limit plain key
+ vk_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, vk_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
+ vk_hex = "caffeecaffeecaffeecaffeecaffee88babebabe";
+ key_size = 16;
+ crypt_decode_key(key, vk_hex, key_size);
+ OK_(prepare_keyfile(KEYFILE1, key, strlen(vk_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, vk_hex), "only key length used");
+ OK_(strncmp(key, vk_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, vk_hex), "only key length used");
+ OK_(strncmp(key, vk_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
+ vk_hex = "aabbcaffeecaffeecaffeecaffeecaff";
+ key_size = 16;
+ crypt_decode_key(key, vk_hex, key_size);
+ OK_(prepare_keyfile(KEYFILE1, key, strlen(vk_hex) / 2));
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+ OK_(crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, key_size, 0));
+ OK_(get_key_dm(CDEVICE_1, key, sizeof(key)));
+ OK_(strcmp(key, vk_hex));
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ _remove_keyfiles();
+
+ // hash sha256
+ params.hash = "sha256";
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_format(cd, CRYPT_PLAIN, "aes", "cbc-essiv:sha256", NULL, NULL, 16, &params));
+
+ // 0 1 2 3 4 5 6 7 8 9 a b c d e f
+ vk_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, vk_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, vk_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, vk_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, &params),
+ "Unsupported block size.");
+ params.data_block_size = 4096;
+ params.hash_block_size = 333;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "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, &params),
+ "Too large salt.");
+ params.salt_size = 32;
+
+ /* hash_type */
+ params.hash_type = 3;
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupported hash type.");
+ params.hash_type = 1;
+ params.hash_name = "blah";
+ FAIL_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params),
+ "Unsupported hash name.");
+ params.hash_name = "sha256";
+
+ OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params));
+ CRYPT_FREE(cd);
+
+ params.data_device = NULL;
+ OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_EMPTY));
+ OK_(crypt_format(cd, CRYPT_VERITY, NULL, NULL, NULL, NULL, 0, &params));
+ EQ_(strcmp(DEVICE_2, crypt_get_metadata_device_name(cd)), 0);
+ CRYPT_FREE(cd);
+
+ /* Verify */
+ OK_(crypt_init(&cd, DEVICE_2));
+ memset(&params, 0, sizeof(params));
+ params.data_device = DEVICE_EMPTY;
+ params.flags = CRYPT_VERITY_CHECK_HASH;
+ OK_(crypt_load(cd, CRYPT_VERITY, &params));
+
+ /* check verity params */
+ EQ_(crypt_get_volume_key_size(cd), 32);
+ OK_(strcmp(CRYPT_VERITY, crypt_get_type(cd)));
+ memset(&params, 0, sizeof(params));
+ OK_(crypt_get_verity_info(cd, &params));
+ OK_(strcmp("sha256", params.hash_name));
+ EQ_(strlen(salt_hex) / 2, params.salt_size);
+ OK_(memcmp(salt, params.salt, params.salt_size));
+ EQ_(4096, params.data_block_size);
+ EQ_(4096, params.hash_block_size);
+ EQ_(1, params.hash_type);
+ EQ_(crypt_get_volume_key_size(cd), 32);
+
+ OK_(crypt_activate_by_volume_key(cd, NULL, root_hash, 32, 0));
+ OK_(crypt_set_data_device(cd, DEVICE_1));
+ FAIL_(crypt_activate_by_volume_key(cd, NULL, root_hash, 32, 0), "Data corrupted");;
+
+ OK_(crypt_set_data_device(cd, DEVICE_EMPTY));
+ if (crypt_activate_by_volume_key(cd, CDEVICE_1, root_hash, 32,
+ CRYPT_ACTIVATE_READONLY) == -ENOTSUP) {
+ printf("WARNING: kernel dm-verity not supported, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY, cad.flags);
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init_by_name(&cd, CDEVICE_1));
+ 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));
+ memset(root_hash_out, 0, root_hash_out_size);
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, root_hash_out, &root_hash_out_size, NULL));
+ 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[64], key_def[64];
+ const char *key_hex =
+ "98dee64abe44bbf41d171c1f7b3e8eacda6d6b01f459097459a167f8c2872a96"
+ "3979531d1cdc18af62757cf22286f16f8583d848524f128d7594ac2082668c73";
+ int r;
+
+ crypt_decode_key(key_def, key_hex, strlen(key_hex) / 2);
+
+ // First ensure we can use af_alg skcipher interface
+ r = crypt_benchmark(NULL, "aes", "xts", 512, 16, 1024, &enc_mbr, &dec_mbr);
+ if (r == -ENOTSUP || r == -ENOENT) {
+ printf("WARNING: algif_skcipher interface not present, skipping test.\n");
+ return;
+ }
+
+ OK_(crypt_init(&cd, tcrypt_dev));
+ params.passphrase_size--;
+ FAIL_(crypt_load(cd, CRYPT_TCRYPT, &params), "Wrong passphrase");
+ params.passphrase_size++;
+ OK_(crypt_load(cd, CRYPT_TCRYPT, &params));
+
+ // check params after load
+ OK_(strcmp("xts-plain64", crypt_get_cipher_mode(cd)));
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ EQ_(key_size, crypt_get_volume_key_size(cd));
+ EQ_(256, crypt_get_iv_offset(cd));
+ EQ_(256, crypt_get_data_offset(cd));
+
+ memset(key, 0, key_size);
+
+ 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));
+ memset(key, 0, key_size);
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key, &key_size, NULL));
+ 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");
+ FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key, &key_size, NULL), "Need crypt_load");
+
+ // check params after init_by_name
+ OK_(strcmp("xts-plain64", crypt_get_cipher_mode(cd)));
+ OK_(strcmp("aes", crypt_get_cipher(cd)));
+ EQ_(key_size, crypt_get_volume_key_size(cd));
+ EQ_(256, crypt_get_iv_offset(cd));
+ EQ_(256, crypt_get_data_offset(cd));
+
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(CRYPT_ACTIVATE_READONLY, cad.flags);
+ EQ_(256, cad.offset);
+ EQ_(256, cad.iv_offset);
+ EQ_(72, cad.size);
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // init with detached header is not supported
+ OK_(crypt_init_data_device(&cd, tcrypt_dev2, DEVICE_2));
+ FAIL_(crypt_load(cd, CRYPT_TCRYPT, &params), "can't use tcrypt with separate metadata device");
+ CRYPT_FREE(cd);
+
+ // Following test uses non-FIPS algorithms in the cipher chain
+ if(_fips_mode)
+ return;
+
+ OK_(crypt_init(&cd, tcrypt_dev2));
+ params.keyfiles = NULL;
+ params.keyfiles_count = 0;
+ r = crypt_load(cd, CRYPT_TCRYPT, &params);
+ if (r < 0) {
+ printf("WARNING: cannot use non-AES encryption, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, CRYPT_ACTIVATE_READONLY));
+ CRYPT_FREE(cd);
+
+ // Deactivate the whole chain
+ EQ_(crypt_status(NULL, CDEVICE_1 "_1"), CRYPT_BUSY);
+ OK_(crypt_deactivate(NULL, CDEVICE_1));
+ EQ_(crypt_status(NULL, CDEVICE_1 "_1"), CRYPT_INACTIVE);
+}
+
+static void ResizeIntegrity(void)
+{
+ struct crypt_params_integrity params = {
+ .tag_size = 4,
+ .integrity = "crc32c",
+ .sector_size = 4096,
+ };
+ int ret;
+ uint64_t r_size, whole_device_size = 0;
+
+ if (!t_dm_integrity_resize_support()) {
+ printf("WARNING: integrity device resize not supported, skipping test.\n");
+ return;
+ }
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, 0));
+ t_device_size(DMDIR CDEVICE_1, &whole_device_size);
+ // shrink the device
+ OK_(crypt_resize(cd, CDEVICE_1, 1024 * 1024 / 512));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1024 * 1024 / 512, r_size >> TST_SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ // fill the whole device again (size = 0)
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(whole_device_size, r_size);
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // detached metadata
+ OK_(create_dmdevice_over_loop(H_DEVICE, 1024 * 1024 / 512));
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DEVICE_2));
+ OK_(crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &whole_device_size))
+ EQ_(10 * 1024 * 1024 / 512, whole_device_size >> TST_SECTOR_SHIFT);
+ // shrink the device
+ OK_(crypt_resize(cd, CDEVICE_1, 1024 * 1024 / 512));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1024 * 1024 / 512, r_size >> TST_SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ // fill the whole device again (size = 0)
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(whole_device_size, r_size);
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void ResizeIntegrityWithKey(void)
+{
+ struct crypt_params_integrity params = {
+ .tag_size = 4,
+ .integrity = "hmac(sha256)",
+ .journal_integrity = "hmac(sha256)",
+ .journal_crypt = "cbc(aes)",
+ .sector_size = 4096,
+ };
+ int ret;
+ uint64_t r_size, whole_device_size = 0;
+
+ const char *key_integrity_hex = "41b06f3968ff10783edf3dd8c31d0d6e";
+ const char *key_journal_integrity_hex = "9a3f924d03ab4a3307b148f844628f59";
+ const char *key_journal_crypt_hex = "087a6943383f6c344cef03695b4f7277";
+
+ char integrity_key[128], journal_integrity_key[128], journal_crypt_key[128];
+
+ size_t integrity_key_size = strlen(key_integrity_hex) / 2;
+ size_t journal_integrity_key_size = strlen(key_journal_integrity_hex) / 2;
+ size_t journal_crypt_key_size = strlen(key_journal_crypt_hex) / 2;
+
+ crypt_decode_key(integrity_key, key_integrity_hex, integrity_key_size);
+ crypt_decode_key(journal_integrity_key, key_journal_integrity_hex, journal_integrity_key_size);
+ crypt_decode_key(journal_crypt_key, key_journal_crypt_hex, journal_crypt_key_size);
+
+ params.integrity_key_size = integrity_key_size;
+
+ params.journal_integrity_key_size = journal_integrity_key_size;
+ params.journal_integrity_key = journal_integrity_key;
+
+ params.journal_crypt_key_size = journal_crypt_key_size;
+ params.journal_crypt_key = journal_crypt_key;
+
+ if (!t_dm_integrity_resize_support()) {
+ printf("WARNING: integrity device resize not supported, skipping test.\n");
+ return;
+ }
+
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, integrity_key, integrity_key_size, 0));
+ t_device_size(DMDIR CDEVICE_1, &whole_device_size);
+ // shrink the device
+ OK_(crypt_resize(cd, CDEVICE_1, 1024*1024/512));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1024*1024/512, r_size >> TST_SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ // fill the whole device again (size = 0)
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(whole_device_size, r_size);
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ // detached metadata
+ OK_(create_dmdevice_over_loop(H_DEVICE, 1024 * 1024 / 512));
+ OK_(crypt_init_data_device(&cd, DMDIR H_DEVICE, DEVICE_2));
+ OK_(crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, integrity_key, integrity_key_size, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &whole_device_size))
+ EQ_(10*1024*1024/512, whole_device_size >> TST_SECTOR_SHIFT);
+ // shrink the device
+ OK_(crypt_resize(cd, CDEVICE_1, 1024*1024/512));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(1024*1024/512, r_size >> TST_SECTOR_SHIFT);
+ FAIL_(crypt_resize(cd, CDEVICE_1, 1001), "Device too small");
+ // fill the whole device again (size = 0)
+ OK_(crypt_resize(cd, CDEVICE_1, 0));
+ if (!t_device_size(DMDIR CDEVICE_1, &r_size))
+ EQ_(whole_device_size, r_size);
+ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE);
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void IntegrityTest(void)
+{
+ struct crypt_params_integrity params = {
+ .tag_size = 4,
+ .integrity = "crc32c",
+ .sector_size = 4096,
+ }, ip = {};
+ struct crypt_active_device cad;
+ int ret;
+
+ // FIXME: this should be more detailed
+
+ OK_(crypt_init(&cd,DEVICE_1));
+ FAIL_(crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,NULL), "params field required");
+ ret = crypt_format(cd,CRYPT_INTEGRITY,NULL,NULL,NULL,NULL,0,&params);
+ if (ret < 0) {
+ printf("WARNING: cannot format integrity device, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ OK_(crypt_get_integrity_info(cd, &ip));
+ EQ_(ip.tag_size, params.tag_size);
+ EQ_(ip.sector_size, params.sector_size);
+ EQ_(crypt_get_sector_size(cd), params.sector_size);
+ EQ_(ip.interleave_sectors, params.interleave_sectors);
+ EQ_(ip.journal_size, params.journal_size);
+ EQ_(ip.journal_watermark, params.journal_watermark);
+ EQ_(ip.integrity_key_size, 0);
+ OK_(strcmp(ip.integrity,params.integrity));
+ FAIL_(crypt_set_uuid(cd,DEVICE_1_UUID),"can't set uuid to integrity device");
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_load(cd, CRYPT_INTEGRITY, NULL));
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ //params.tag_size = 8;
+ //FAIL_(crypt_load(cd, CRYPT_INTEGRITY, &params), "tag size mismatch");
+ params.tag_size = 4;
+ OK_(crypt_load(cd, CRYPT_INTEGRITY, &params));
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, 0));
+ 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)));
+
+ if (t_dm_integrity_recalculate_support()) {
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(cad.flags & CRYPT_ACTIVATE_RECALCULATE, 0);
+ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, NULL, 0, CRYPT_ACTIVATE_REFRESH | CRYPT_ACTIVATE_RECALCULATE));
+ OK_(crypt_get_active_device(cd, CDEVICE_1, &cad));
+ EQ_(cad.flags & CRYPT_ACTIVATE_RECALCULATE, CRYPT_ACTIVATE_RECALCULATE);
+ }
+
+ OK_(crypt_deactivate(cd, CDEVICE_1));
+ CRYPT_FREE(cd);
+}
+
+static void WipeTest(void)
+{
+ OK_(crypt_init(&cd, NULL));
+ FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL), "No device");
+ FAIL_(crypt_wipe(cd, DEVICE_WRONG, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL), "Wrong device");
+ OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL));
+ OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_RANDOM, 0, 4096, 0, 0, NULL, NULL));
+ OK_(crypt_wipe(cd, DEVICE_1, CRYPT_WIPE_RANDOM, 0, 4096, 0, CRYPT_WIPE_NO_DIRECT_IO, NULL, NULL));
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DEVICE_1));
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_ZERO, 0, 4096, 0, 0, NULL, NULL));
+ OK_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096, TST_SECTOR_SIZE, 0, NULL, NULL));
+ FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096, TST_SECTOR_SIZE-1, 0, NULL, NULL), "Sector size");
+ FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 0, 4096 - 1, 0, 0, NULL, NULL), "Length size not aligned");
+ FAIL_(crypt_wipe(cd, NULL, CRYPT_WIPE_RANDOM, 1, 4096, 0, 0, NULL, NULL), "Offset not aligned");
+ CRYPT_FREE(cd);
+}
+
+static void LuksKeyslotAdd(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
+ };
+ struct crypt_pbkdf_type min_pbkdf2 = {
+ .type = "pbkdf2",
+ .hash = "sha256",
+ .iterations = 1000,
+ .flags = CRYPT_PBKDF_NO_BENCHMARK
+ };
+ char key[128], key3[128];
+
+ const char *passphrase = PASSPHRASE, *passphrase2 = "nsdkFI&Y#.sd";
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ const char *vk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+ struct crypt_keyslot_context *um1, *um2;
+
+ crypt_decode_key(key, vk_hex, key_size);
+ crypt_decode_key(key3, vk_hex2, key_size);
+
+ // init test devices
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
+
+ // test support for embedded key (after crypt_format)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, NULL, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase, strlen(passphrase), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+ CRYPT_FREE(cd);
+
+ // test add by volume key
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase2, strlen(passphrase2), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, CRYPT_ANY_SLOT, um2, 0), 0);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // Add by same passphrase
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase, strlen(passphrase), &um1));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um1, 0), 1);
+ crypt_keyslot_context_free(um1);
+
+ // new passphrase can't be provided by key method
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase, strlen(passphrase), &um1));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, 1, um1, CRYPT_ANY_SLOT, um2, 0), "Can't get passphrase via selected unlock method");
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // add by keyfile
+ OK_(prepare_keyfile(KEYFILE1, passphrase2, strlen(passphrase2)));
+ OK_(prepare_keyfile(KEYFILE2, KEY1, strlen(KEY1)));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, 0, um1, 2, um2, 0), 2);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // add by same keyfile
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um1));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 4, um1, 0), 4);
+ crypt_keyslot_context_free(um1);
+
+ // keyslot already exists
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, passphrase2, strlen(passphrase2), &um1));
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, 3, um1, 0, um2, 0), "Keyslot already exists.");
+ crypt_keyslot_context_free(um2);
+
+ // flags not supported with LUKS1
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, CRYPT_ANY_SLOT, um2, CRYPT_VOLUME_KEY_NO_SEGMENT), "Not supported with LUKS1.");
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ // LUKS2 token not supported
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um1));
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, 2, um1, CRYPT_ANY_SLOT, um2, 0), "Not supported with LUKS1.");
+ EQ_(crypt_keyslot_context_get_error(um2), -EINVAL);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE2, 0, 0, &um1));
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um2));
+ FAIL_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um2, CRYPT_ANY_SLOT, um1, 0), "Not supported with LUKS1.");
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+
+ CRYPT_FREE(cd);
+
+ _cleanup_dmdevices();
+}
+
+static void VolumeKeyGet(void)
+{
+ struct crypt_params_luks1 params = {
+ .hash = "sha512",
+ .data_alignment = 2048, // 2M, data offset will be 2048
+ };
+ struct crypt_pbkdf_type min_pbkdf2 = {
+ .type = "pbkdf2",
+ .hash = "sha256",
+ .iterations = 1000,
+ .flags = CRYPT_PBKDF_NO_BENCHMARK
+ };
+ char key[128], key2[128];
+
+ const char *vk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
+ size_t key_size = strlen(vk_hex) / 2;
+ const char *cipher = "aes";
+ const char *cipher_mode = "cbc-essiv:sha256";
+ uint64_t r_payload_offset;
+ struct crypt_keyslot_context *um1, *um2;
+
+ crypt_decode_key(key, vk_hex, key_size);
+
+ OK_(prepare_keyfile(KEYFILE1, PASSPHRASE1, strlen(PASSPHRASE1)));
+
+ // init test devices
+ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset));
+ OK_(create_dmdevice_over_loop(H_DEVICE, r_payload_offset + 1));
+
+ // test support for embedded key (after crypt_format)
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ key_size--;
+ FAIL_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL), "buffer too small");
+
+ // check cached generated volume key can be retrieved
+ key_size++;
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
+ OK_(crypt_volume_key_verify(cd, key2, key_size));
+ CRYPT_FREE(cd);
+
+ // check we can add keyslot via retrieved key
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 3, um2, 0), 3);
+ crypt_keyslot_context_free(um1);
+ crypt_keyslot_context_free(um2);
+ CRYPT_FREE(cd);
+
+ // check selected volume key can be retrieved and added
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_set_pbkdf_type(cd, &min_pbkdf2));
+ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params));
+ memset(key2, 0, key_size);
+ OK_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, NULL));
+ OK_(memcmp(key, key2, key_size));
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key2, key_size, &um1));
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 0, um2, 0), 0);
+ crypt_keyslot_context_free(um2);
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um2));
+ EQ_(crypt_keyslot_add_by_keyslot_context(cd, CRYPT_ANY_SLOT, um1, 1, um2, 0), 1);
+ crypt_keyslot_context_free(um2);
+ crypt_keyslot_context_free(um1);
+ CRYPT_FREE(cd);
+
+ OK_(crypt_init(&cd, DMDIR H_DEVICE));
+ OK_(crypt_load(cd, CRYPT_LUKS1, NULL));
+ // check key context is not usable
+ OK_(crypt_keyslot_context_init_by_volume_key(cd, key, key_size, &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
+ crypt_keyslot_context_free(um1);
+
+ // check token context is not usable
+ OK_(crypt_keyslot_context_init_by_token(cd, CRYPT_ANY_TOKEN, NULL, NULL, 0, NULL, &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), -EINVAL);
+ crypt_keyslot_context_free(um1);
+
+ // by passphrase
+ memset(key2, 0, key_size);
+ OK_(crypt_keyslot_context_init_by_passphrase(cd, PASSPHRASE, strlen(PASSPHRASE), &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 0);
+ OK_(memcmp(key, key2, key_size));
+ memset(key2, 0, key_size);
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, 0, key2, &key_size, um1), 0);
+ OK_(memcmp(key, key2, key_size));
+ crypt_keyslot_context_free(um1);
+
+ // by keyfile
+ memset(key2, 0, key_size);
+ OK_(crypt_keyslot_context_init_by_keyfile(cd, KEYFILE1, 0, 0, &um1));
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, CRYPT_ANY_SLOT, key2, &key_size, um1), 1);
+ OK_(memcmp(key, key2, key_size));
+ memset(key2, 0, key_size);
+ EQ_(crypt_volume_key_get_by_keyslot_context(cd, 1, key2, &key_size, um1), 1);
+ crypt_keyslot_context_free(um1);
+ CRYPT_FREE(cd);
+
+ _remove_keyfiles();
+ _cleanup_dmdevices();
+}
+
+// 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, &params));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params),
+ "Already formatted.");
+ CRYPT_FREE(cd);
+
+ params.hash = "whirlpool";
+ OK_(crypt_init(&cd, DEVICE_2));
+ ret = crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params);
+ if (ret < 0) {
+ printf("WARNING: whirlpool not supported, skipping test.\n");
+ CRYPT_FREE(cd);
+ return;
+ }
+ CRYPT_FREE(cd);
+
+ params.hash = "md5";
+ OK_(crypt_init(&cd, DEVICE_2));
+ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, &params),
+ "MD5 unsupported, too short");
+ CRYPT_FREE(cd);
+}
+
+static void int_handler(int sig __attribute__((__unused__)))
+{
+ _quit++;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sigaction sa = { .sa_handler = int_handler };
+ int i;
+
+ if (getuid() != 0) {
+ printf("You must be root to run this test.\n");
+ exit(77);
+ }
+#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");
+ RUN_(ResizeIntegrity, "Integrity raw resize");
+ RUN_(ResizeIntegrityWithKey, "Integrity raw resize with key");
+ RUN_(WipeTest, "Wipe device");
+ RUN_(LuksKeyslotAdd, "Adding keyslot via new API");
+ RUN_(VolumeKeyGet, "Getting volume key via keyslot context API");
+
+ _cleanup();
+ return 0;
+}
diff --git a/tests/api_test.h b/tests/api_test.h
new file mode 100644
index 0000000..14efead
--- /dev/null
+++ b/tests/api_test.h
@@ -0,0 +1,165 @@
+/*
+ * cryptsetup library API check functions
+ *
+ * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2023 Milan Broz
+ * Copyright (C) 2016-2023 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 t_dm_integrity_resize_support(void);
+int t_dm_integrity_recalculate_support(void);
+int t_dm_capi_string_supported(void);
+int t_set_readahead(const char *device, unsigned value);
+
+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)
+
+/* to silent clang -Wcast-align when working with byte arrays */
+#define VOIDP_CAST(x) (x)(void*)
+
+#define DMDIR "/dev/mapper/"
+
+#define TST_SECTOR_SHIFT 9L
+#define TST_SECTOR_SIZE 512
+#define TST_LOOP_FILE_SIZE (((1 << 20) * 100) >> TST_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 */
+#define T_DM_SECTOR_SIZE_SUPPORTED (1 << 13) /* support for sector size setting in dm-crypt/dm-integrity */
+#define T_DM_CAPI_STRING_SUPPORTED (1 << 14) /* support for cryptoapi format cipher definition */
+#define T_DM_DEFERRED_SUPPORTED (1 << 15) /* deferred removal of device */
+#define T_DM_INTEGRITY_RECALC_SUPPORTED (1 << 16) /* dm-integrity automatic recalculation supported */
+#define T_DM_INTEGRITY_BITMAP_SUPPORTED (1 << 17) /* dm-integrity bitmap mode supported */
+#define T_DM_GET_TARGET_VERSION_SUPPORTED (1 << 18) /* dm DM_GET_TARGET version ioctl supported */
+#define T_DM_INTEGRITY_FIX_PADDING_SUPPORTED (1 << 19) /* supports the parameter fix_padding that fixes a bug that caused excessive padding */
+#define T_DM_BITLK_EBOIV_SUPPORTED (1 << 20) /* EBOIV for BITLK supported */
+#define T_DM_BITLK_ELEPHANT_SUPPORTED (1 << 21) /* Elephant diffuser for BITLK supported */
+#define T_DM_VERITY_SIGNATURE_SUPPORTED (1 << 22) /* Verity option root_hash_sig_key_desc supported */
+#define T_DM_INTEGRITY_DISCARDS_SUPPORTED (1 << 23) /* dm-integrity discards/TRIM option is supported */
+#define T_DM_INTEGRITY_RESIZE_SUPPORTED (1 << 23) /* dm-integrity resize of the integrity device supported (introduced in the same version as discards)*/
+#define T_DM_VERITY_PANIC_CORRUPTION_SUPPORTED (1 << 24) /* dm-verity panic on corruption */
+#define T_DM_CRYPT_NO_WORKQUEUE_SUPPORTED (1 << 25) /* dm-crypt suppot for bypassing workqueues */
+#define T_DM_INTEGRITY_FIX_HMAC_SUPPORTED (1 << 26) /* hmac covers also superblock */
+#define T_DM_INTEGRITY_RESET_RECALC_SUPPORTED (1 << 27) /* dm-integrity automatic recalculation supported */
+#define T_DM_VERITY_TASKLETS_SUPPORTED (1 << 28) /* dm-verity tasklets supported */
+
+/* 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);
+
+int t_device_size_by_devno(dev_t devno, uint64_t *retval);
+int t_get_devno(const char *dev, dev_t *devno);
+
+typedef enum { ERR_RD = 0, ERR_WR, ERR_RW, ERR_REMOVE } error_io_info;
+
+int dmdevice_error_io(const char *dm_name,
+ const char *dm_device,
+ const char *error_device,
+ uint64_t data_offset,
+ uint64_t offset,
+ uint64_t length,
+ error_io_info ei);
+
+#endif
diff --git a/tests/bitlk-compat-test b/tests/bitlk-compat-test
new file mode 100755
index 0000000..8559e06
--- /dev/null
+++ b/tests/bitlk-compat-test
@@ -0,0 +1,200 @@
+#!/bin/bash
+
+# check bitlk images parsing
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+TST_DIR=bitlk-images
+MAP=bitlktst
+DUMP_VK_FILE=bitlk-test-vk
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP
+ rm -rf $TST_DIR
+}
+
+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."
+ remove_mapping
+ 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
+
+ # volume size
+ dump_size=$(echo "$dump" | grep "Volume size:" | cut -d: -f2 | tr -d "\t\n ")
+ [ "$dump_size" = "104857600[bytes]" -o "$dump_size" = "134217728[bytes]" ] || fail " volume size check from dump failed."
+
+ # description
+ dump_desc=$(echo "$dump" | grep Description: | cut -d: -f2 | tr -d "\t\n ")
+ [ "${dump_desc:0:7}" = "DESKTOP" -o "${dump_desc:0:3}" = "WIN" ] || fail " Description check from dump failed."
+
+ # 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()
+{
+ command -v valgrind >/dev/null || 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
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ ! -d $TST_DIR ] && tar xJSf $srcdir/bitlk-images.tar.xz --no-same-owner 2>/dev/null || skip "Incompatible tar."
+
+[ -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."
+ remove_mapping
+ exit 0
+fi
+
+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=$(blkid -p -o value -s 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
+
+ # test with volume key
+ rm -f $DUMP_VK_FILE >/dev/null 2>&1
+ echo -n " $file"
+ echo $PASSPHRASE | $CRYPTSETUP bitlkDump -r $file --dump-volume-key --volume-key-file $DUMP_VK_FILE >/dev/null 2>&1
+ ret=$?
+ [ $ret -eq 0 ] || fail " failed to dump volume key"
+ $CRYPTSETUP bitlkOpen -r $file $MAP --volume-key-file $DUMP_VK_FILE >/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 using volume key ($ret)"
+ $CRYPTSETUP status $MAP >/dev/null || fail
+ $CRYPTSETUP status /dev/mapper/$MAP >/dev/null || fail
+ uuid=$(blkid -p -o value -s 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]"
+ rm -f $DUMP_VK_FILE >/dev/null 2>&1
+
+ # 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=$(blkid -p -o value -s 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
+
+remove_mapping
+exit 0
diff --git a/tests/bitlk-images.tar.xz b/tests/bitlk-images.tar.xz
new file mode 100644
index 0000000..845e9de
--- /dev/null
+++ b/tests/bitlk-images.tar.xz
Binary files differ
diff --git a/tests/blkid-luks2-pv.img.xz b/tests/blkid-luks2-pv.img.xz
new file mode 100644
index 0000000..c9d0e57
--- /dev/null
+++ b/tests/blkid-luks2-pv.img.xz
Binary files differ
diff --git a/tests/blockwise-compat-test b/tests/blockwise-compat-test
new file mode 100755
index 0000000..11db493
--- /dev/null
+++ b/tests/blockwise-compat-test
@@ -0,0 +1,379 @@
+#!/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 >/dev/null 2>&1
+ if [ -b "$1" ]; then
+ sleep .1
+ _tries=$((_tries-1))
+ fi
+ done
+
+ test ! -b "$1" || rmmod scsi_debug >/dev/null 2>&1
+}
+
+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 77
+}
+
+add_device() {
+ rmmod scsi_debug >/dev/null 2>&1
+ if [ -d /sys/module/scsi_debug ] ; then
+ skip "Cannot use scsi_debug module (in use or compiled-in)."
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ skip "This kernel seems to not support proper scsi_debug module."
+ fi
+ grep -q scsi_debug /sys/block/*/device/model || sleep 2
+ 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))
+}
+
+command -v $STRACE >/dev/null || 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-args-test b/tests/compat-args-test
new file mode 100755
index 0000000..c41e942
--- /dev/null
+++ b/tests/compat-args-test
@@ -0,0 +1,299 @@
+#!/bin/bash
+
+PS4='$LINENO:'
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+TEST_UUID="12345678-1234-1234-1234-123456789abc"
+
+TFILE=test-args.out
+
+function cleanup()
+{
+ rm -f $TFILE 2> /dev/null
+}
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED backtrace:"
+ while caller $frame; do ((frame++)); done
+ cleanup
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ echo "Test skipped."
+ cleanup
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 xxx()
+{
+ $CRYPTSETUP --test-args $@ > $TFILE 2>&1
+ local ret=$?
+
+ grep -q -e ": unknown option\|Argument <action> missing" $TFILE && {
+ echo "'$CRYPTSETUP --test-args $@' command:"
+ cat $TFILE
+ fail "Probably typo in test"
+ }
+ test $ret -ne 0 || fail
+}
+
+function exp_fail()
+{
+ # xxx $@
+ $CRYPTSETUP --test-args $@ 2>/dev/null && fail
+}
+
+function exp_pass()
+{
+ $CRYPTSETUP --test-args $@ >/dev/null || fail
+}
+
+export LANG=C
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+# initial test constructed according to current cryptsetup content
+echo "[1] Current state"
+exp_fail resize NAME --test-passphrase
+exp_fail close NAME --test-passphrase
+exp_pass open DEV NAME --test-passphrase --type bitlk
+exp_pass open DEV NAME --test-passphrase --type luks
+exp_pass open DEV NAME --test-passphrase --type luks1
+exp_pass open DEV NAME --test-passphrase --type luks2
+exp_fail open DEV NAME --test-passphrase --type plain
+
+exp_fail open DEV NAME --deferred
+exp_pass close NAME --deferred
+
+exp_pass open DEV NAME --type plain --shared
+exp_fail open DEV NAME --type luks1 --shared
+exp_fail close NAME --shared
+
+exp_pass open DEV NAME --allow-discards
+exp_fail close NAME --allow-discards
+
+exp_fail close NAME --persistent
+exp_pass open DEV NAME --persistent
+exp_fail open DEV NAME --persistent --test-passphrase
+
+exp_fail luksFormat DEV --serialize-memory-hard-pbkdf
+exp_pass open DEV NAME --serialize-memory-hard-pbkdf
+
+exp_pass reencrypt DEV --key-size 32
+exp_fail reencrypt DEV --key-size 31
+exp_fail reencrypt DEV --key-size -32
+exp_pass luksFormat DEV --key-size 32
+exp_fail luksFormat DEV --key-size 31
+exp_fail luksFormat DEV --key-size -32
+exp_pass open DEV NAME --key-size 32 # --type plain -c aes-xts-plain64
+exp_fail open DEV NAME --key-size 31 # --type plain -c aes-xts-plain64
+exp_pass benchmark --key-size 32
+exp_fail benchmark --key-size 31
+exp_pass luksAddKey DEV --key-size 32 # --unbound
+exp_fail luksAddKey DEV --key-size 31 # --unbound
+
+exp_fail close NAME --key-size 32
+exp_fail luksUUID DEV --key-size 32
+
+# bug
+# exp_fail luksFormat DEV --type luks1 --integrity hmac-sha256
+exp_pass luksFormat DEV --type luks2 --integrity hmac-sha256
+exp_fail open DEV NAME --integrity hmac-sha256
+
+exp_pass luksFormat DEV --type luks2 --integrity hmac-sha256 --integrity-no-wipe
+exp_fail luksFormat DEV --type luks2 --integrity-no-wipe
+# bug
+# exp_fail luksFormat DEV --type luks1 --integrity hmac-sha256 --integrity-no-wipe
+exp_fail open DEV NAME --integrity-no-wipe
+exp_fail open DEV NAME --integrity-no-wipe --integrity hmac-sha256
+
+exp_pass luksFormat --label L --subsystem S DEV # --type luks2
+exp_pass luksFormat --label L DEV # --type luks2
+exp_pass luksFormat --subsystem S DEV # --type luks2
+exp_pass config --label L --subsystem S DEV
+exp_pass config --label L DEV
+exp_pass config --subsystem S DEV
+# bug
+#exp_fail luksFormat --label L --subsystem S DEV --type luks1
+#exp_fail luksFormat --label L DEV --type luks1
+#exp_fail luksFormat --subsystem S DEV --type luks1
+exp_fail open DEV NAME --label L --subsystem S
+exp_fail open DEV NAME --label L
+exp_fail open DEV NAME --subsystem S
+
+exp_fail luksFormat DEV -S-2
+# bug
+# exp_fail luksFormat DEV -S-1
+
+# prob. many bug: accepts --[new-]keyfile-size w/o --[new-]key-file
+exp_pass luksFormat DEV --keyfile-size 42 --key-file F
+exp_fail luksFormat DEV --keyfile-size -1 --key-file F
+# bug (is it? e.g. empty passphrase)
+# exp_fail luksFormat DEV --keyfile-size 0
+exp_pass luksAddKey DEV --keyfile-size 42 --key-file F --new-keyfile-size 42 NF
+exp_fail luksAddKey DEV --new-keyfile-size -42 NF
+exp_fail luksAddKey DEV --keyfile-size 42 --key-file F --new-keyfile-size -42 NF
+exp_fail luksFormat DEV --keyfile-size -1 --key-file F
+# bug (is it? e.g. empty passphrase)
+# exp_fail luksFormat DEV --keyfile-size 0
+
+exp_fail open DEV NAME --key-file F0 --key-file F1
+exp_pass open DEV NAME --key-file F0 --key-file F1 --type tcrypt
+
+# why? (luksAddKey fail)
+exp_fail luksAddKey DEV --use-random
+exp_fail luksAddKey DEV --use-urandom
+exp_fail luksAddKey DEV --use-urandom --use-random
+exp_fail luksFormat DEV --use-urandom --use-random
+exp_pass luksFormat DEV --use-random
+exp_pass luksFormat DEV --use-urandom
+
+exp_fail open DEV NAME --uuid $TEST_UUID
+exp_pass luksFormat DEV --uuid $TEST_UUID
+exp_pass luksUUID DEV --uuid $TEST_UUID
+
+exp_fail open DEV NAME --align-payload 8192
+exp_fail open DEV NAME --align-payload 8292 --type plain
+exp_pass luksFormat DEV --align-payload 8192
+exp_fail luksFormat DEV --align-payload 8192 --offset 16384
+exp_fail luksFormat DEV --align-payload 8192 --offset 8192
+
+exp_fail resize NAME --luks2-metadata-size 16k
+exp_fail resize NAME --luks2-keyslots-size 16m
+exp_pass luksFormat DEV --luks2-keyslots-size 16m
+exp_pass luksFormat DEV --luks2-metadata-size 16k
+exp_pass reencrypt DEV --luks2-keyslots-size 16m
+exp_pass reencrypt DEV --luks2-metadata-size 16k
+
+exp_fail luksFormat DEV --skip 8192
+exp_fail open DEV NAME --skip 8192
+exp_pass open DEV NAME --skip 8192 --type plain
+exp_pass open DEV NAME --skip 8192 --type loopaes
+
+exp_fail resize NAME --offset 8292
+exp_pass luksFormat DEV --offset 16384
+exp_fail open DEV NAME --offset 16384
+exp_pass open DEV NAME --offset 16384 --type plain
+exp_pass open DEV NAME --offset 16384 --type loopaes
+
+exp_fail open DEV NAME --tcrypt-hidden
+exp_fail open DEV NAME --tcrypt-system
+exp_fail open DEV NAME --tcrypt-backup
+# bug
+# exp_fail open DEV NAME --tcrypt-hidden --tcrypt-system --tcrypt-backup --type tcrypt
+exp_pass open DEV NAME --tcrypt-hidden --type tcrypt
+exp_pass open DEV NAME --tcrypt-backup --type tcrypt
+exp_pass open DEV NAME --tcrypt-system --type tcrypt
+exp_pass tcryptDump DEV NAME --tcrypt-hidden --type tcrypt
+exp_pass tcryptDump DEV NAME --tcrypt-backup --type tcrypt
+exp_pass tcryptDump DEV NAME --tcrypt-system --type tcrypt
+exp_fail tcryptDump DEV NAME --allow-discards --tcrypt-hidden --type tcrypt
+
+# bug
+# exp_fail close NAME --type tcrypt --veracrypt
+exp_fail open DEV NAME --veracrypt
+exp_pass open DEV NAME --type tcrypt --veracrypt
+exp_pass open DEV NAME --type tcrypt --veracrypt --veracrypt-pim 1
+exp_fail open DEV NAME --type tcrypt --veracrypt --veracrypt-pim -2
+exp_fail open DEV NAME --type tcrypt --disable-veracrypt --veracrypt-pim 1
+exp_fail open DEV NAME --type tcrypt --veracrypt --veracrypt-pim -1
+exp_fail open DEV NAME --type tcrypt --disable-veracrypt --veracrypt-query-pim
+exp_fail open DEV NAME --type tcrypt --disable-veracrypt --veracrypt-query-pim --veracrypt-pim 1
+exp_fail open DEV NAME --disable-veracrypt --veracrypt-query-pim
+
+# bug
+# exp_fail open DEV NAME --priority normal
+exp_fail config DEV --priority normal
+exp_fail config DEV -S1 --priority norma
+exp_pass config DEV -S1 --priority normal
+exp_pass config DEV -S1 --priority ignore
+exp_pass config DEV -S1 --priority prefer
+
+# bug
+# exp_fail open DEV NAME --pbkdf argon2i
+exp_fail luksFormat DEV --pbkdf blah
+exp_pass luksFormat DEV --pbkdf argon2i
+exp_pass luksFormat DEV --pbkdf pbkdf2
+exp_pass luksFormat DEV --pbkdf argon2id
+exp_fail luksFormat DEV --type luks2 --pbkdf-force-iterations 4 -i1
+exp_fail luksFormat DEV --type luks1 --pbkdf-force-iterations 1001 -i1
+
+exp_fail open DEV NAME --sector-size 1024
+exp_pass open DEV NAME --type plain --sector-size 1024
+# bug
+# exp_fail luksFormat DEV --sector-size 0
+exp_fail luksFormat DEV --sector-size 511
+exp_fail luksFormat DEV --sector-size 8192
+exp_pass reencrypt DEV --sector-size 1024
+exp_pass luksFormat DEV --sector-size 1024
+
+exp_fail luksFormat DEV --iv-large-sectors
+exp_fail open DEV --type tcrypt --iv-large-sectors
+exp_fail open DEV --type plain --iv-large-sectors --sector-size 512
+exp_pass open DEV --type plain --iv-large-sectors --sector-size 1024
+
+exp_fail luksAddKey DEV --unbound
+exp_fail luksAddKey DEV --unbound --key-size 0
+exp_pass luksAddKey DEV --unbound --key-size 8
+exp_pass luksDump DEV --unbound -S5
+exp_fail luksDump DEV --unbound
+exp_pass open DEV --unbound --test-passphrase
+exp_pass open DEV --unbound --test-passphrase -S5
+exp_fail open DEV --unbound NAME
+exp_fail open DEV --unbound -S5 NAME
+
+exp_fail resize NAME --refresh
+exp_fail open DEV NAME --test-passphrase --refresh
+exp_pass open DEV NAME --refresh
+exp_pass refresh DEV NAME
+exp_fail refresh DEV NAME --test-passphrase
+
+# bug
+# exp_fail luksFormat DEV --reduce-device-size 64m
+exp_fail reencrypt DEV --reduce-device-size 2G # max 1g
+exp_fail reencrypt DEV --reduce-device-size $((64*1024*1024+1))
+exp_fail reencrypt DEV --reduce-device-size -64m
+exp_pass reencrypt DEV --reduce-device-size 64m
+exp_fail reencrypt DEV --reduce-device-size 64m --device-size 100g
+# bugs
+# exp_fail open DEV --decrypt --header H
+# exp_fail open DEV --encrypt
+# exp_fail open DEV NAME --device-size 32m
+# exp_fail open DEV NAME --size 100
+exp_pass open DEV NAME --device-size 32m --type plain
+exp_fail open DEV NAME --device-size $((32*1024*1024+1)) --type plain
+exp_pass open DEV NAME --size 100 --type plain
+exp_fail open DEV NAME --size 100 --device-size $((512*100)) --type plain
+exp_fail reencrypt DEV --device-size $((32*1024*1024+1))
+exp_pass reencrypt DEV --device-size 32m
+
+exp_fail luksFormat DEV NAME --keyslot-cipher ks
+exp_fail luksFormat DEV NAME --keyslot-key-size 32
+exp_pass luksFormat DEV NAME --keyslot-cipher ks --keyslot-key-size 32
+# bugs
+# exp_fail open DEV NAME --keyslot-cipher ks --keyslot-key-size 32
+# exp_fail luksFormat --type luks1 DEV NAME --keyslot-cipher ks --keyslot-key-size 32
+
+cleanup
+exit 0
diff --git a/tests/compat-test b/tests/compat-test
new file mode 100755
index 0000000..6dc8004
--- /dev/null
+++ b/tests/compat-test
@@ -0,0 +1,1137 @@
+#!/bin/bash
+
+PS4='$LINENO:'
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+CRYPTSETUP_RAW=$CRYPTSETUP
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+DIFFER=./differ
+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)
+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 >/dev/null 2>&1
+ 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
+ [ -n "$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 >/dev/null 2>&1
+ if [ -b "$1" ]; then
+ sleep .1
+ _tries=$((_tries-1))
+ fi
+ done
+
+ test ! -b "$1" || rmmod scsi_debug >/dev/null 2>&1
+}
+
+function add_scsi_device() {
+ scsi_debug_teardown $DEV
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 1
+ DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+function valgrind_setup()
+{
+ [ -n "$VALG" ] || return
+ command -v valgrind >/dev/null || 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
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+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-volume-key 2>/dev/null && fail
+echo $PWDW | $CRYPTSETUP luksDump $IMG --dump-master-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $IMG --dump-volume-key | grep -q "MK dump:" || fail
+echo $PWD1 | $CRYPTSETUP luksDump $IMG --dump-master-key | grep -q "MK dump:" || fail
+$CRYPTSETUP luksDump -q $IMG --dump-volume-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
+rm -f $VK_FILE
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-volume-key --volume-key-file $VK_FILE >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-volume-key --volume-key-file $VK_FILE 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --volume-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."
+[ ! -x "$DIFFER" ] && skip "Cannot find $DIFFER, test skipped."
+
+# LUKS root-tests
+prepare "[1] open - compat image - acceptance check" new
+echo $PWD0 | $CRYPTSETUP luksOpen $LOOPDEV $DEV_NAME || fail
+check_exists
+ORG_SHA256=$(sha256sum -b /dev/mapper/$DEV_NAME | cut -f 1 -d' ')
+[ "$ORG_SHA256" = 7428e8f2436882a07eb32765086f5c899474c08b5576f556b573d2aabdf923e8 ] || 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
+if [ ! fips_mode ] ; then
+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
+fi
+
+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 --volume-key-file /dev/urandom $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --volume-key-file /dev/urandom $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT --volume-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
+if [ -d /dev/disk/by-uuid ] ; then
+ 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
+fi
+# skip tests using empty passphrase
+if [ ! fips_mode ]; then
+# 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
+fi
+# open by volume key
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT -s 256 --volume-key-file $KEY1 $LOOPDEV || fail
+$CRYPTSETUP luksOpen --volume-key-file /dev/urandom $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen --volume-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
+# volumekey
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV --volume-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 -q $FAST_PBKDF_OPT $LOOPDEV --volume-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 -q $FAST_PBKDF_OPT $LOOPDEV --volume-key-file /dev/null --key-slot 5 2>/dev/null && fail
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT $LOOPDEV --volume-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 -q $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 3 2>/dev/null && fail
+# keyfile/keyfile
+$CRYPTSETUP luksAddKey -q $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 -q $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 -q $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 -q $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 2 --new-keyfile-size 8 || 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 -q $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 sha256 --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 sha256 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha256 $LOOPDEV || fail
+$CRYPTSETUP -q remove $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP -q create $DEV_NAME --hash sha256 --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 --hash sha256 $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 sha256 $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 sha256 $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 sha256 --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-volume-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $LOOPDEV --dump-volume-key | grep -q "MK dump:" || fail
+$CRYPTSETUP luksDump -q $LOOPDEV --dump-volume-key -d $KEY1 | grep -q "MK dump:" || fail
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-volume-key --volume-key-file $VK_FILE > /dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --volume-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 -q $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 sha256 --offset 0 --size 256 || fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha256 --offset 512 --size 256 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --hash sha256 --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 sha256 $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
+# skip tests using empty passphrase
+if [ ! fips_mode ]; then
+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
+fi
+
+prepare "[27] luksOpen/luksResume 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 luksSuspend $DEV_NAME || fail
+echo $PWD3 | $CRYPTSETUP luksResume -S 4 $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail
+echo $PWD3 | $CRYPTSETUP luksResume -S 5 $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo -e "$PWD3\n$PWD1" | $CRYPTSETUP luksAddKey -q $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 -q $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 luksSuspend $DEV_NAME || fail
+$CRYPTSETUP luksResume -S 1 -d $KEY5 $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail
+$CRYPTSETUP luksResume -S 5 -d $KEY5 $DEV_NAME || fail
+$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 -q $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
+# fix ecb-plain
+$CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF_OPT $LOOPDEV $KEY1 --hash sha256 -c aes-ecb || fail
+echo -n "ecb-xxx" | dd of=$LOOPDEV bs=1 seek=40 >/dev/null 2>&1
+$CRYPTSETUP -q repair $LOOPDEV >/dev/null 2>&1 || fail
+$CRYPTSETUP luksOpen -d $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+# fix uppercase hash
+echo -n "SHA256" | dd of=$LOOPDEV bs=1 seek=72 >/dev/null 2>&1
+$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 -q $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 --cancel-deferred $DEV_NAME >/dev/null 2>&1
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" >/dev/null 2>&1 && fail
+ $CRYPTSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+ $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).
+command -v expect >/dev/null || 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=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 -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."
+
+prepare "[41] New luksAddKey options." file
+rm -f $VK_FILE
+echo "$PWD1" | $CRYPTSETUP luksFormat --type luks1 $FAST_PBKDF_OPT $IMG || fail
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-volume-key --volume-key-file $VK_FILE >/dev/null || fail
+
+# pass pass
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -q -S1 $FAST_PBKDF_OPT $IMG || fail
+echo $PWD2 | $CRYPTSETUP open -q --test-passphrase -S1 $IMG || fail
+
+# pass file
+echo "$PWD2" | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S1 --new-key-slot 2 $IMG $KEY1 || fail
+$CRYPTSETUP open --test-passphrase -q -S2 -d $KEY1 $IMG || fail
+
+# file pass
+echo "$PWD3" | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S2 -d $KEY1 --new-key-slot 3 $IMG || fail
+echo $PWD3 | $CRYPTSETUP open -q --test-passphrase -S3 $IMG || fail
+
+# file file
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S2 --new-key-slot 4 -d $KEY1 --new-keyfile $KEY2 $IMG || fail
+$CRYPTSETUP open --test-passphrase -q -S4 -d $KEY2 $IMG || fail
+
+# vk pass
+echo $PWD3 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S5 --volume-key-file $VK_FILE $IMG || fail
+echo $PWD3 | $CRYPTSETUP open -q --test-passphrase -S5 $IMG || fail
+
+# vk file
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S6 --volume-key-file $VK_FILE --new-keyfile $KEY5 $IMG || fail
+$CRYPTSETUP open --test-passphrase -q -S6 -d $KEY5 $IMG || fail
+
+remove_mapping
+exit 0
diff --git a/tests/compat-test2 b/tests/compat-test2
new file mode 100755
index 0000000..c54dc7e
--- /dev/null
+++ b/tests/compat-test2
@@ -0,0 +1,1204 @@
+#!/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)
+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 >/dev/null 2>&1
+ 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()
+{
+ command -v valgrind >/dev/null || 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() {
+ command -v keyctl >/dev/null || 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
+ if $($CRYPTSETUP --version | grep -q "BLKID"); then
+ HAVE_BLKID=1
+ else
+ HAVE_BLKID=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 >/dev/null 2>&1
+ if [ -b "$1" ]; then
+ sleep .1
+ _tries=$((_tries-1))
+ fi
+ done
+
+ test ! -b "$1" || rmmod scsi_debug >/dev/null 2>&1
+}
+
+function add_scsi_device() {
+ scsi_debug_teardown $DEV
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 1
+ DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+export LANG=C
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ $(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
+# Check JSON dump for some mandatory section
+$CRYPTSETUP -q luksDump $LOOPDEV --dump-json-metadata | grep -q '"tokens":' || 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 --volume-key-file /dev/urandom --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --volume-key-file /dev/urandom --type luks2 $LOOPDEV -d $KEY1 || fail
+$CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --volume-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
+if [ -d /dev/disk/by-uuid ] ; then
+ 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
+fi
+# skip tests using empty passphrases
+if [ ! fips_mode ]; then
+# 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
+fi
+
+# open by volume key
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT -s 256 --volume-key-file $KEY1 --type luks2 $LOOPDEV || fail
+$CRYPTSETUP luksOpen --volume-key-file /dev/urandom $LOOPDEV $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP luksOpen --volume-key-file $KEY1 $LOOPDEV $DEV_NAME || fail
+$CRYPTSETUP -q luksClose $DEV_NAME || fail
+
+prepare "[17] AddKey volume key, passphrase and keyfile" wipe
+# volumekey
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 $LOOPDEV --volume-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 -q $FAST_PBKDF_OPT $LOOPDEV --volume-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 -q $FAST_PBKDF_OPT $LOOPDEV --volume-key-file /dev/null --key-slot 5 2>/dev/null && fail
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT $LOOPDEV --volume-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 -q $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 --key-slot 3 2>/dev/null && fail
+# keyfile/keyfile
+$CRYPTSETUP luksAddKey -q $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 -q $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 -q $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 -q $FAST_PBKDF_OPT $LOOPDEV $KEY1 --key-slot 2 --new-keyfile-size 8 || 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-volume-key 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksDump $LOOPDEV --dump-volume-key | grep -q "MK dump:" || fail
+$CRYPTSETUP luksDump -q $LOOPDEV --dump-volume-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
+rm -f $VK_FILE
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-volume-key --volume-key-file $VK_FILE >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksDump -q $LOOPDEV --dump-volume-key --volume-key-file $VK_FILE 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --volume-key-file $VK_FILE $LOOPDEV || fail
+# Use volume key file without keyslots
+$CRYPTSETUP luksErase -q $LOOPDEV || fail
+$CRYPTSETUP luksOpen --volume-key-file $VK_FILE --key-size 256 --test-passphrase $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT --volume-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 --key-size 256 --luks2-keyslots-size 256k >/dev/null || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey -q $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] - with LUKS2 it should stay
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY2 $KEY1 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: 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
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "2: luks2" && fail
+# test out of raw area, change in-place (space only for 2 keyslots)
+$CRYPTSETUP luksChangeKey $LOOPDEV $FAST_PBKDF_OPT -d $KEY1 $KEY2 || fail
+$CRYPTSETUP luksDump $LOOPDEV | grep -q "0: luks2" || 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/Resume 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 luksSuspend $DEV_NAME || fail
+echo $PWD3 | $CRYPTSETUP luksResume -S 4 $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail
+echo $PWD3 | $CRYPTSETUP luksResume -S 5 $DEV_NAME || fail
+$CRYPTSETUP luksClose $DEV_NAME || fail
+echo -e "$PWD3\n$PWD1" | $CRYPTSETUP luksAddKey -q $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 -q $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP luksOpen -S 5 -d $KEY5 $LOOPDEV $DEV_NAME || fail
+check_exists
+$CRYPTSETUP luksSuspend $DEV_NAME || fail
+$CRYPTSETUP luksResume -S 1 -d $KEY5 $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" || fail
+$CRYPTSETUP luksResume -S 5 -d $KEY5 $DEV_NAME || fail
+$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 --unbound --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 --unbound --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 --sector-size 512 || 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 -q $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
+if [ "$HAVE_BLKID" -gt 0 ]; then
+ $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
+fi
+$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 -q $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 -q $FAST_PBKDF_OPT -S 1 -d $KEY5 $LOOPDEV $KEY1 || fail
+$CRYPTSETUP -q luksDump $LOOPDEV --dump-json-metadata >/dev/null 2>&1 && 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 sha512 || fail
+$CRYPTSETUP luksAddKey -q $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
+
+# keyslot 1 area offset is higher than keyslot 0 area
+echo $PWD1 | $CRYPTSETUP -q luksFormat $FAST_PBKDF_OPT --type luks2 --key-slot 0 $LOOPDEV || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_OPT --key-slot 1 $LOOPDEV || fail
+echo -e "$PWD1\n$PWD1" | $CRYPTSETUP -q luksChangeKey $FAST_PBKDF_OPT $LOOPDEV || fail
+# convert to LUKS1 and back; LUKS1 does not store length, only offset
+$CRYPTSETUP -q convert --type luks1 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q open --test-passphrase $LOOPDEV || fail
+echo $PWD2 | $CRYPTSETUP -q open --test-passphrase $LOOPDEV || fail
+$CRYPTSETUP -q convert --type luks2 $LOOPDEV || fail
+echo $PWD1 | $CRYPTSETUP -q open --test-passphrase $LOOPDEV || fail
+echo $PWD2 | $CRYPTSETUP -q open --test-passphrase $LOOPDEV || 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 luksSuspend $DEV_NAME || fail
+ $CRYPTSETUP luksResume $DEV_NAME <&- || fail
+ $CRYPTSETUP -q status $DEV_NAME | grep -q "(suspended)" && fail
+ $CRYPTSETUP luksSuspend $DEV_NAME || fail
+ $CRYPTSETUP luksResume $DEV_NAME --token-type luks2-keyring <&- || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ # check --token-type sort of works (TODO: extend tests when native systemd tokens are available)
+ echo -n "$IMPORT_TOKEN" | $CRYPTSETUP token import $LOOPDEV --token-id 22 || fail
+ # this excludes keyring tokens from unlocking device
+ $CRYPTSETUP open --token-only --token-type some_type $LOOPDEV --test-passphrase && fail
+ $CRYPTSETUP open --token-only --token-type some_type $LOOPDEV $DEV_NAME && fail
+ $CRYPTSETUP status $DEV_NAME > /dev/null && 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 -q -S4 $FAST_PBKDF_OPT $LOOPDEV || fail
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN1 --key-slot 4 --token-id 0 || fail
+ $CRYPTSETUP -q luksKillSlot $LOOPDEV 4 || fail
+ $CRYPTSETUP token remove --token-id 0 $LOOPDEV || fail
+
+ # test we can add unassigned token
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN0 --unbound --token-id 0 || fail
+ $CRYPTSETUP open --token-only --token-id 0 --test-passphrase $LOOPDEV && fail
+ $CRYPTSETUP token remove --token-id 0 $LOOPDEV || fail
+
+ # test token unassign works
+ $CRYPTSETUP token add $LOOPDEV --key-description $TEST_TOKEN0 -S0 --token-id 0 || fail
+ $CRYPTSETUP open --token-only --token-id 0 --test-passphrase $LOOPDEV || fail
+ $CRYPTSETUP token unassign --token-id 0 $LOOPDEV 2>/dev/null && fail
+ $CRYPTSETUP token unassign -S0 $LOOPDEV 2>/dev/null && fail
+ $CRYPTSETUP token unassign --token-id 0 -S0 $LOOPDEV || fail
+ $CRYPTSETUP open --token-only --token-id 0 --test-passphrase $LOOPDEV && fail
+ $CRYPTSETUP token unassign --token-id 0 -S0 $LOOPDEV 2>/dev/null && fail
+ $CRYPTSETUP token unassign --token-id 0 -S44 $LOOPDEV 2>/dev/null && fail
+ $CRYPTSETUP token unassign --token-id 44 -S0 $LOOPDEV 2>/dev/null && 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 >$TOKEN_FILE1 || fail
+diff $TOKEN_FILE0 $TOKEN_FILE1 || fail
+$CRYPTSETUP token export $LOOPDEV --token-id 11 >$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
+$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 -q $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 -q $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 -q $FAST_PBKDF_OPT -S 21 --unbound -s 72 $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 72 $LOOPDEV || fail
+echo $PWD2 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT --unbound -s 72 -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 -q $FAST_PBKDF_OPT --unbound -s 512 -S 3 --volume-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 --volume-key-file /dev/urandom $LOOPDEV 2> /dev/null && fail
+# do not allow one 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 -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
+# 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 -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 -q $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 --volume-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 --volume-key-file $KEY_FILE1 -S3 $LOOPDEV > /dev/null || fail
+diff $KEY_FILE0 $KEY_FILE1 || fail
+echo $PWD3 | $CRYPTSETUP luksDump --unbound --volume-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 --volume-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 -q -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 >$TOKEN_FILE1 || fail
+ diff $TOKEN_FILE1 $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 -q $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 -q $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 -q $FAST_PBKDF_OPT --key-slot 21 --unbound -s 72 --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 -q $FAST_PBKDF_OPT --key-slot 22 --unbound -s 72 $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
+
+prepare "[43] New luksAddKey options." wipe
+rm -f $VK_FILE
+echo "$PWD1" | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_OPT $IMG || fail
+echo $PWD1 | $CRYPTSETUP luksDump -q $IMG --dump-volume-key --volume-key-file $VK_FILE >/dev/null || fail
+
+# pass pass
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -q -S1 $FAST_PBKDF_OPT $IMG || fail
+echo $PWD2 | $CRYPTSETUP open -q --test-passphrase -S1 $IMG || fail
+
+# pass file
+echo "$PWD2" | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S1 --new-key-slot 2 $IMG $KEY1 || fail
+$CRYPTSETUP open --test-passphrase -q -S2 -d $KEY1 $IMG || fail
+
+# file pass
+echo "$PWD3" | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S2 -d $KEY1 --new-key-slot 3 $IMG || fail
+echo $PWD3 | $CRYPTSETUP open -q --test-passphrase -S3 $IMG || fail
+
+# file file
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S2 --new-key-slot 4 -d $KEY1 --new-keyfile $KEY2 $IMG || fail
+$CRYPTSETUP open --test-passphrase -q -S4 -d $KEY2 $IMG || fail
+
+# vk pass
+echo $PWD4 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S5 --volume-key-file $VK_FILE $IMG || fail
+echo $PWD4 | $CRYPTSETUP open -q --test-passphrase -S5 $IMG || fail
+
+# vk file
+$CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S6 --volume-key-file $VK_FILE --new-keyfile $KEY5 $IMG || fail
+$CRYPTSETUP open --test-passphrase -q -S6 -d $KEY5 $IMG || fail
+
+if [ $HAVE_KEYRING -gt 0 -a -d /proc/sys/kernel/keys ]; then
+ test_and_prepare_keyring
+ load_key user $TEST_TOKEN0 $PWD1 "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
+ load_key user $TEST_TOKEN1 $PWDW "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
+ $CRYPTSETUP token add $IMG --key-description $TEST_TOKEN0 --token-id 0 -S0 || fail
+ $CRYPTSETUP token add $IMG --key-description $TEST_TOKEN1 --token-id 1 --unbound || fail
+
+ # pass token
+ echo -e "$PWD1" | $CRYPTSETUP luksAddKey -q -S7 --new-token-id 1 $FAST_PBKDF_OPT $IMG || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 7 || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG && fail
+
+ # file token
+ $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S2 --new-key-slot 7 --new-token-id 1 -d $KEY1 $IMG || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 7 || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG && fail
+
+ # vk token
+ $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S7 --volume-key-file $VK_FILE --new-token-id 1 $IMG || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 7 || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG && fail
+
+ # token pass
+ echo $PWD4 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S7 --token-id 0 $IMG || fail
+ echo $PWD4 | $CRYPTSETUP open -q --test-passphrase -S7 $IMG || fail
+
+ # token file
+ echo $PWD4 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S8 --token-id 0 $IMG $KEY2 || fail
+ $CRYPTSETUP open -q --test-passphrase -S8 --key-file $KEY2 $IMG || fail
+
+ # token token
+ $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S9 --token-id 0 --new-token-id 1 $IMG || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 9 || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 1 -q $IMG && fail
+
+ # reuse same token
+ $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT -S0 --new-key-slot 9 --token-id 0 --new-token-id 0 $IMG || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 0 -q $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 9 || fail
+
+ # reuse same token
+ $CRYPTSETUP luksAddKey -q $FAST_PBKDF_OPT --token-id 0 --new-token-id 0 $IMG || fail
+ echo $PWD1 | $CRYPTSETUP luksKillSlot $IMG 9 || fail
+ $CRYPTSETUP open -q --test-passphrase --token-only --token-id 0 -q $IMG || fail
+fi
+
+remove_mapping
+exit 0
diff --git a/tests/compatimage.img.xz b/tests/compatimage.img.xz
new file mode 100644
index 0000000..cb515f4
--- /dev/null
+++ b/tests/compatimage.img.xz
Binary files differ
diff --git a/tests/compatimage2.img.xz b/tests/compatimage2.img.xz
new file mode 100644
index 0000000..ceaeafc
--- /dev/null
+++ b/tests/compatimage2.img.xz
Binary files differ
diff --git a/tests/compatv10image.img.xz b/tests/compatv10image.img.xz
new file mode 100644
index 0000000..2203626
--- /dev/null
+++ b/tests/compatv10image.img.xz
Binary files differ
diff --git a/tests/conversion_imgs.tar.xz b/tests/conversion_imgs.tar.xz
new file mode 100644
index 0000000..43e35fe
--- /dev/null
+++ b/tests/conversion_imgs.tar.xz
Binary files differ
diff --git a/tests/crypto-vectors.c b/tests/crypto-vectors.c
new file mode 100644
index 0000000..ae8dd68
--- /dev/null
+++ b/tests/crypto-vectors.c
@@ -0,0 +1,1559 @@
+/*
+ * cryptsetup crypto backend test vectors
+ *
+ * Copyright (C) 2018-2023 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 <unistd.h>
+#include <fcntl.h>
+
+#include "crypto_backend/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);
+}
+
+static bool fips_mode(void)
+{
+ int fd;
+ char buf = 0;
+
+ fd = open("/proc/sys/crypto/fips_enabled", O_RDONLY);
+
+ if (fd < 0)
+ return false;
+
+ if (read(fd, &buf, 1) != 1)
+ buf = '0';
+
+ close(fd);
+
+ return (buf == '1');
+}
+
+/*
+ * 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
+ },
+ /* empty password */
+ {
+ "argon2i", NULL, 0, 3, 128, 1,
+ "", 0,
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "\xbb\x1f\xf2\xb9\x9f\xd4\x4a\xd9"
+ "\xdf\x7f\xb9\x54\x55\x9e\xb8\xeb"
+ "\xb5\x9d\xab\xce\x2e\x62\x9f\x9b"
+ "\x89\x09\xfe\xde\x57\xcc\x63\x86", 32
+ },
+ {
+ "argon2id", NULL, 0, 3, 128, 1,
+ "", 0,
+ "\x00\x01\x02\x03\x04\x05\x06\x07"
+ "\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f", 16,
+ "\x09\x2f\x38\x35\xac\xb2\x43\x92"
+ "\x93\xeb\xcd\xe8\x04\x16\x6a\x31"
+ "\xce\x14\xd4\x55\xdb\xd8\xf7\xe6"
+ "\xb4\xf5\x9d\x64\x8e\xd0\x3a\xdb", 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"
+ },
+}}};
+
+/* Base64 test vectors */
+struct base64_test_vector {
+ size_t decoded_len;
+ const char *decoded;
+ const char *encoded;
+};
+
+static struct base64_test_vector base64_test_vectors[] = {
+ { 0, "", "" },
+ { 1, "\x00", "AA==" },
+ { 1, "f", "Zg==" },
+ { 2, "fo", "Zm8=" },
+ { 3, "foo", "Zm9v" },
+ { 4, "foob", "Zm9vYg==" },
+ { 5, "fooba", "Zm9vYmE=" },
+ { 6, "foobar", "Zm9vYmFy" },
+ { 11, "Hello world", "SGVsbG8gd29ybGQ=" },
+ { 22, "\x36\x03\x84\xdc\x4e\x03\x46\xa0\xb5\x2d\x03"
+ "\x6e\xd0\x56\xed\xa0\x37\x02\xac\xc6\x65\xd1",
+ "NgOE3E4DRqC1LQNu0FbtoDcCrMZl0Q==" },
+ { 3, "***", "Kioq" },
+ { 4, "\x01\x02\x03\x04", "AQIDBA==" },
+ { 5, "\xAD\xAD\xAD\xAD\xAD", "ra2tra0=" },
+ { 5, "\xFF\xFF\xFF\xFF\xFF", "//////8=" },
+ { 32, "\x40\xC1\x3F\xBD\x05\x4C\x72\x2A\xA3\xC2\xF2"
+ "\x11\x73\xC0\x69\xEA\x49\x7D\x35\x29\x6B\xCC"
+ "\x24\x65\xF6\xF9\xD0\x41\x08\x7B\xD7\xA9",
+ "QME/vQVMciqjwvIRc8Bp6kl9NSlrzCRl9vnQQQh716k=" },
+ { 7, "\x54\x0f\xdc\xf0\x0f\xaf\x4a", "VA/c8A+vSg==" },
+ {179, "blah blah blah blah blah blah blah blah blah "
+ "blah blah blah blah blah blah blah blah blah "
+ "blah blah blah blah blah blah blah blah blah "
+ "blah blah blah blah blah blah blah blah blah",
+ "YmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxh"
+ "aCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBi"
+ "bGFoIGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFo"
+ "IGJsYWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJs"
+ "YWggYmxhaCBibGFoIGJsYWggYmxhaCBibGFoIGJsYWgg"
+ "YmxhaCBibGFoIGJsYWg=" },
+};
+
+/* UTF8 to UTF16LE test vectors */
+struct utf8_16_test_vector {
+ size_t len8;
+ size_t len16;
+ const char *utf8;
+ const char *utf16;
+};
+
+static struct utf8_16_test_vector utf8_16_test_vectors[] = {
+ { 1, 2, "a", "\x61\x00" },
+ { 16, 32, "0123456789abcdef",
+ "\x30\x00\x31\x00\x32\x00\x33\x00\x34\x00\x35\x00\x36\x00\x37\x00"
+ "\x38\x00\x39\x00\x61\x00\x62\x00\x63\x00\x64\x00\x65\x00\x66\x00" },
+ { 77, 78,
+ "\xf2\xa4\xa5\x94\x49\xf2\xa1\x98\x98\xd8\x8a\xe1\xb4\x88\xea\xa7"
+ "\xaa\xde\x95\xe2\x85\xb1\xe7\xb1\x9a\xf2\xb5\xa1\xae\x37\x2d\xd0"
+ "\xa9\xe1\x9a\x9c\xe8\xb0\xb7\xc8\x95\x0a\xf3\xaa\x92\xba\xf2\x83"
+ "\xb0\x99\xf0\x9b\xbe\x8f\x4f\xc8\x86\x30\xe7\xab\xa0\xda\xb9\xd8"
+ "\x89\xd8\xbc\xd7\x8a\xd9\xbc\xc3\x8f\x33\x62\xda\xb7",
+ "\x52\xda\x54\xdd\x49\x00\x45\xda\x18\xde\x0a\x06\x08\x1d\xea\xa9"
+ "\x95\x07\x71\x21\x5a\x7c\x96\xda\x6e\xdc\x37\x00\x2d\x00\x29\x04"
+ "\x9c\x16\x37\x8c\x15\x02\x0a\x00\x69\xdb\xba\xdc\xcf\xd9\x19\xdc"
+ "\x2f\xd8\x8f\xdf\x4f\x00\x06\x02\x30\x00\xe0\x7a\xb9\x06\x09\x06"
+ "\x3c\x06\xca\x05\x7c\x06\xcf\x00\x33\x00\x62\x00\xb7\x06" },
+};
+
+static int pbkdf_test_vectors(void)
+{
+ char result[256];
+ unsigned int i;
+ const struct kdf_test_vector *vec;
+
+ for (i = 0; i < ARRAY_SIZE(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);
+
+
+ if (r) {
+ crypt_hash_destroy(h);
+ 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);
+ crypt_hash_destroy(h);
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * After crypt_hash_final() the context must be reset, repeat
+ */
+ crypt_backend_memzero(result, sizeof(result));
+ r = crypt_hash_write(h, vector->data, vector->data_length);
+ if (!r)
+ r = crypt_hash_final(h, result, vector->out[j].length);
+
+ if (r || memcmp(result, vector->out[j].out, vector->out[j].length)) {
+ printf("[FAILED (RESET CONTEXT)]\n");
+ printhex(" got", result, vector->out[j].length);
+ printhex("want", vector->out[j].out, vector->out[j].length);
+ crypt_hash_destroy(h);
+ return EXIT_FAILURE;
+ }
+
+ crypt_hash_destroy(h);
+ }
+ 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);
+
+ if (r) {
+ crypt_hmac_destroy(hmac);
+ 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);
+ crypt_hmac_destroy(hmac);
+ return EXIT_FAILURE;
+ }
+
+ /*
+ * After crypt_hmac_final() the context must be reset, repeat
+ */
+ crypt_backend_memzero(result, sizeof(result));
+ r = crypt_hmac_write(hmac, vector->data, vector->data_length);
+ if (!r)
+ r = crypt_hmac_final(hmac, result, vector->out[j].length);
+
+ if (r || memcmp(result, vector->out[j].out, vector->out[j].length)) {
+ printf("[FAILED (RESET CONTEXT)]\n");
+ printhex(" got", result, vector->out[j].length);
+ printhex("want", vector->out[j].out, vector->out[j].length);
+ crypt_hmac_destroy(hmac);
+ return EXIT_FAILURE;
+ }
+
+ crypt_hmac_destroy(hmac);
+ }
+ 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;
+
+ if (snprintf(mode_iv, sizeof(mode_iv)-2, "%s-%s", vector->cipher_mode, vector->iv_name) < 0)
+ return EXIT_FAILURE;
+
+ 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 int check_hash(const char *hash)
+{
+ struct crypt_hash *h;
+
+ if (crypt_hash_size(hash) < 0)
+ return EXIT_FAILURE;
+
+ if (crypt_hash_init(&h, hash))
+ return EXIT_FAILURE;
+
+ crypt_hash_destroy(h);
+ return EXIT_SUCCESS;
+}
+
+static int base64_test(void)
+{
+ unsigned int i;
+ char *s;
+ size_t s_len;
+
+ for (i = 0; i < ARRAY_SIZE(base64_test_vectors); i++) {
+ printf("BASE64 %02d ", i);
+ s = NULL;
+ s_len = 0;
+ if (crypt_base64_encode(&s, &s_len,
+ base64_test_vectors[i].decoded,
+ base64_test_vectors[i].decoded_len) < 0) {
+ printf("[ENCODE FAILED]\n");
+ return EXIT_FAILURE;
+ } else if (strcmp(s, base64_test_vectors[i].encoded)) {
+ printf("[ENCODE FAILED]\n");
+ free(s);
+ return EXIT_FAILURE;
+ }
+ printf("[encode]");
+ free(s);
+
+ s = NULL;
+ s_len = 0;
+ if (crypt_base64_decode(&s, &s_len,
+ base64_test_vectors[i].encoded,
+ strlen(base64_test_vectors[i].encoded)) < 0) {
+ printf("[DECODE FAILED]\n");
+ return EXIT_FAILURE;
+ } else if (s_len != base64_test_vectors[i].decoded_len ||
+ memcmp(s, base64_test_vectors[i].decoded, s_len)) {
+ printf("[DECODE FAILED]\n");
+ return EXIT_FAILURE;
+ }
+ printf("[decode]\n");
+ free(s);
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int utf8_16_test(void)
+{
+ unsigned int i;
+ char s8[128], *s;
+ char16_t c16[256], s16[256], *su;
+
+ for (i = 0; i < ARRAY_SIZE(utf8_16_test_vectors); i++) {
+ printf("UTF8/16 %02d ", i);
+ crypt_backend_memzero(s16, sizeof(s16));
+ su = &s16[0];
+ if (crypt_utf8_to_utf16(&su, utf8_16_test_vectors[i].utf8,
+ utf8_16_test_vectors[i].len8) < 0 ||
+ memcmp(utf8_16_test_vectors[i].utf16, s16,
+ utf8_16_test_vectors[i].len16)) {
+ printf("[UTF8_TO_UTF16 FAILED]\n");
+ return EXIT_FAILURE;
+ }
+ printf("[UTF8_TO_UTF16]");
+
+ crypt_backend_memzero(s8, sizeof(s8));
+ s = &s8[0];
+ memcpy(c16, utf8_16_test_vectors[i].utf16, utf8_16_test_vectors[i].len16);
+ if (crypt_utf16_to_utf8(&s, c16, utf8_16_test_vectors[i].len16) < 0 ||
+ utf8_16_test_vectors[i].len8 != strlen(s8) ||
+ memcmp(utf8_16_test_vectors[i].utf8, s8,
+ utf8_16_test_vectors[i].len8)) {
+ printf("[UTF16_TO_UTF8 FAILED]\n");
+ return EXIT_FAILURE;
+ }
+ printf("[UTF16_TO_UTF8]\n");
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int default_alg_test(void)
+{
+ printf("Defaults: [LUKS1 hash %s] ", DEFAULT_LUKS1_HASH);
+ if (check_hash(DEFAULT_LUKS1_HASH))
+ return EXIT_FAILURE;
+
+ printf("[PLAIN hash %s] ", DEFAULT_PLAIN_HASH);
+ if (check_hash(DEFAULT_PLAIN_HASH))
+ return EXIT_FAILURE;
+
+ printf("[VERITY hash %s] ", DEFAULT_VERITY_HASH);
+ if (check_hash(DEFAULT_VERITY_HASH))
+ return EXIT_FAILURE;
+
+ printf("[OK]\n");
+
+ return EXIT_SUCCESS;
+}
+
+static int memcmp_test(void)
+{
+ printf("MEMEQ ");
+ if (!crypt_backend_memeq("aaaaaaaa", "bbbbbbbb", 8))
+ return EXIT_FAILURE;
+ if (crypt_backend_memeq("aaaaaaaa", "aaaaaaaa", 8))
+ return EXIT_FAILURE;
+ printf("[OK]\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(fips_mode()))
+ 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);
+
+ if (base64_test())
+ exit_test("BASE64 test failed.", EXIT_FAILURE);
+
+ if (memcmp_test())
+ exit_test("Memcmp test failed.", EXIT_FAILURE);
+
+ if (utf8_16_test())
+ exit_test("UTF8/16 test failed.", EXIT_FAILURE);
+
+ if (default_alg_test()) {
+ if (fips_mode())
+ printf("\nDefault compiled-in algorithms test ignored (FIPS mode on).\n");
+ else
+ exit_test("\nDefault compiled-in algorithms 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..fc9913a
--- /dev/null
+++ b/tests/cryptsetup-valg-supps
@@ -0,0 +1,14 @@
+# Suppression 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..c8b53bb
--- /dev/null
+++ b/tests/device-test
@@ -0,0 +1,363 @@
+#!/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
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+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 >/dev/null 2>&1
+}
+
+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
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+add_device() {
+ rmmod scsi_debug >/dev/null 2>&1
+ [ -d /sys/module/scsi_debug ] && skip "Cannot use scsi_debug module (in use or compiled-in)."
+
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ [ $? -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."
+}
+
+add_image()
+{
+ dd if=/dev/zero of=$DEV bs=1M count=32 >/dev/null 2>&1
+}
+
+function dm_crypt_features()
+{
+ modprobe dm-crypt >/dev/null 2>&1 || 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
+{
+ add_image
+
+ 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 --new-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
+}
+
+check_io()
+{
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=1M count=32 iflag=direct 2>/dev/null || fail
+ dd if=/dev/zero of=/dev/mapper/$DEV_NAME bs=1M count=32 oflag=direct 2>/dev/null || fail
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+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."
+
+add_image
+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
+ check_io
+ $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
+ check_io
+ $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
+ check_io
+ fi
+ $CRYPTSETUP close $DEV_NAME || fail
+ echo
+
+ format luks1
+ 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
+ add_device dev_size_mb=1 sector_size=512 num_tgts=1 lbpu=1
+ echo -n "$F"
+ # 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 >/dev/null 2>&1
+ echo -n "[OK] "
+done
+echo
+
+cleanup
+exit 0
diff --git a/tests/differ.c b/tests/differ.c
new file mode 100644
index 0000000..95da8e5
--- /dev/null
+++ b/tests/differ.c
@@ -0,0 +1,166 @@
+/*
+ * cryptsetup file differ check (rewritten Clemens' fileDiffer in Python)
+ *
+ * Copyright (C) 2010-2023 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..870f74d
--- /dev/null
+++ b/tests/discards-test
@@ -0,0 +1,113 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+DEV_NAME="discard-t3st"
+DEV=""
+PWD1="93R4P4pIqAH8"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+cleanup() {
+ [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove --retry $DEV_NAME
+ udevadm settle >/dev/null 2>&1
+ rmmod scsi_debug >/dev/null 2>&1
+ sleep 2
+}
+
+fail()
+{
+ echo "FAILED backtrace:"
+ while caller $frame; do ((frame++)); done
+ cleanup
+ exit 100
+}
+
+skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+add_device() {
+ rmmod scsi_debug >/dev/null 2>&1
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 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
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run this test, test skipped."
+ exit 77
+fi
+
+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 sha256 --allow-discards || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+$CRYPTSETUP resize $DEV_NAME --size 100 || fail
+$CRYPTSETUP status $DEV_NAME | grep flags | grep discards >/dev/null || fail
+dmsetup table $DEV_NAME | grep allow_discards >/dev/null || fail
+$CRYPTSETUP remove $DEV_NAME || fail
+
+cleanup
diff --git a/tests/evil_hdr-keyslot_overlap.xz b/tests/evil_hdr-keyslot_overlap.xz
new file mode 100644
index 0000000..6d56368
--- /dev/null
+++ b/tests/evil_hdr-keyslot_overlap.xz
Binary files differ
diff --git a/tests/evil_hdr-luks_hdr_damage.xz b/tests/evil_hdr-luks_hdr_damage.xz
new file mode 100644
index 0000000..b0b5254
--- /dev/null
+++ b/tests/evil_hdr-luks_hdr_damage.xz
Binary files differ
diff --git a/tests/evil_hdr-payload_overwrite.xz b/tests/evil_hdr-payload_overwrite.xz
new file mode 100644
index 0000000..b9e0e68
--- /dev/null
+++ b/tests/evil_hdr-payload_overwrite.xz
Binary files differ
diff --git a/tests/evil_hdr-small_luks_device.xz b/tests/evil_hdr-small_luks_device.xz
new file mode 100644
index 0000000..700392d
--- /dev/null
+++ b/tests/evil_hdr-small_luks_device.xz
Binary files differ
diff --git a/tests/evil_hdr-stripes_payload_dmg.xz b/tests/evil_hdr-stripes_payload_dmg.xz
new file mode 100644
index 0000000..dc91556
--- /dev/null
+++ b/tests/evil_hdr-stripes_payload_dmg.xz
Binary files differ
diff --git a/tests/fake_systemd_tpm_path.c b/tests/fake_systemd_tpm_path.c
new file mode 100644
index 0000000..6d82989
--- /dev/null
+++ b/tests/fake_systemd_tpm_path.c
@@ -0,0 +1,17 @@
+#include <string.h>
+#include <stdlib.h>
+
+/* systemd tpm2-util.h */
+int tpm2_find_device_auto(int log_level, char **ret);
+
+extern int tpm2_find_device_auto(int log_level __attribute__((unused)), char **ret)
+{
+ const char *path = getenv("TPM_PATH");
+
+ if (!path)
+ *ret = NULL;
+ else
+ *ret = strdup(path);
+
+ return 0;
+}
diff --git a/tests/fake_token_path.c b/tests/fake_token_path.c
new file mode 100644
index 0000000..7b2bad3
--- /dev/null
+++ b/tests/fake_token_path.c
@@ -0,0 +1,6 @@
+#include <libcryptsetup.h>
+
+const char *crypt_token_external_path(void)
+{
+ return BUILD_DIR;
+}
diff --git a/tests/fuzz/FuzzerInterface.h b/tests/fuzz/FuzzerInterface.h
new file mode 100644
index 0000000..b238253
--- /dev/null
+++ b/tests/fuzz/FuzzerInterface.h
@@ -0,0 +1,81 @@
+// Based on https://github.com/llvm-mirror/compiler-rt/blob/master/lib/fuzzer/FuzzerInterface.h
+//
+//===- FuzzerInterface.h - Interface header for the Fuzzer ------*- C++ -* ===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+// Define the interface between libFuzzer and the library being tested.
+//===----------------------------------------------------------------------===//
+
+// NOTE: the libFuzzer interface is thin and in the majority of cases
+// you should not include this file into your target. In 95% of cases
+// all you need is to define the following function in your file:
+// extern "C" int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+// WARNING: keep the interface in C.
+
+#ifndef LLVM_FUZZER_INTERFACE_H
+#define LLVM_FUZZER_INTERFACE_H
+
+#include <stddef.h>
+#include <stdint.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+// Define FUZZER_INTERFACE_VISIBILITY to set default visibility in a way that
+// doesn't break MSVC.
+#if defined(_WIN32)
+#define FUZZER_INTERFACE_VISIBILITY __declspec(dllexport)
+#else
+#define FUZZER_INTERFACE_VISIBILITY __attribute__((visibility("default")))
+#endif
+
+// Mandatory user-provided target function.
+// Executes the code under test with [Data, Data+Size) as the input.
+// libFuzzer will invoke this function *many* times with different inputs.
+// Must return 0.
+FUZZER_INTERFACE_VISIBILITY int
+LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size);
+
+// Optional user-provided initialization function.
+// If provided, this function will be called by libFuzzer once at startup.
+// It may read and modify argc/argv.
+// Must return 0.
+FUZZER_INTERFACE_VISIBILITY int LLVMFuzzerInitialize(int *argc, char ***argv);
+
+// Optional user-provided custom mutator.
+// Mutates raw data in [Data, Data+Size) inplace.
+// Returns the new size, which is not greater than MaxSize.
+// Given the same Seed produces the same mutation.
+FUZZER_INTERFACE_VISIBILITY size_t
+LLVMFuzzerCustomMutator(uint8_t *Data, size_t Size, size_t MaxSize,
+ unsigned int Seed);
+
+// Optional user-provided custom cross-over function.
+// Combines pieces of Data1 & Data2 together into Out.
+// Returns the new size, which is not greater than MaxOutSize.
+// Should produce the same mutation given the same Seed.
+FUZZER_INTERFACE_VISIBILITY size_t
+LLVMFuzzerCustomCrossOver(const uint8_t *Data1, size_t Size1,
+ const uint8_t *Data2, size_t Size2, uint8_t *Out,
+ size_t MaxOutSize, unsigned int Seed);
+
+// Experimental, may go away in future.
+// libFuzzer-provided function to be used inside LLVMFuzzerCustomMutator.
+// Mutates raw data in [Data, Data+Size) inplace.
+// Returns the new size, which is not greater than MaxSize.
+FUZZER_INTERFACE_VISIBILITY size_t
+LLVMFuzzerMutate(uint8_t *Data, size_t Size, size_t MaxSize);
+
+#undef FUZZER_INTERFACE_VISIBILITY
+
+#ifdef __cplusplus
+} // extern "C"
+#endif // __cplusplus
+
+#endif // LLVM_FUZZER_INTERFACE_H
diff --git a/tests/fuzz/LUKS2.proto b/tests/fuzz/LUKS2.proto
new file mode 100644
index 0000000..3a0f287
--- /dev/null
+++ b/tests/fuzz/LUKS2.proto
@@ -0,0 +1,379 @@
+/*
+ * cryptsetup LUKS2 custom mutator
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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.
+ */
+
+syntax = "proto2";
+
+package LUKS2_proto;
+
+// ---------------------------------------------------------------------------
+// ----------------------------- GENERIC OBJECTS -----------------------------
+// ---------------------------------------------------------------------------
+
+message object_id {
+ oneof id {
+ // int_id will be mapped to range -16 to 16 (mod 33)
+ // this way iy should be easier to generate valid
+ // object cross-references
+ uint32 int_id = 1;
+ string string_id = 2;
+ }
+}
+
+message string_uint64 {
+ required bool negative = 1;
+ oneof number {
+ uint32 uint_num = 2;
+ string string_num = 3;
+ }
+}
+
+enum hash_algorithm {
+ HASH_ALG_SHA1 = 1;
+ HASH_ALG_SHA256 = 2;
+}
+
+
+// ---------------------------------------------------------------------------
+// ----------------------------- BINARY HEADER -------------------------------
+// ---------------------------------------------------------------------------
+
+enum luks2_magic {
+ INVALID = 0;
+ FIRST = 1;
+ SECOND = 2;
+}
+
+enum luks_version {
+ ONE = 1;
+ TWO = 2;
+ THREE = 3;
+}
+
+// we limit the size to 64KiB to make the fuzzing faster
+// because the checksum needs to be calculated for the whole image
+enum hdr_size {
+ size_16_KB = 16384;
+ size_32_KB = 32768;
+ size_64_KB = 65536;
+// size_128_KB = 131072;
+// size_256_KB = 262144;
+// size_512_KB = 524288;
+// size_1_MB = 1048576;
+// size_2_MB = 2097152;
+// size_4_MB = 4194304;
+}
+
+enum seqid_description {
+ PRIMARY_GREATER = 0;
+ SECONDARY_GREATER = 1;
+ EQUAL = 2;
+}
+
+// message luks2_hdr_disk {
+// char magic[LUKS2_MAGIC_L];
+// //uint16_t version; /* Version 2 */
+// uint64_t hdr_size; /* in bytes, including JSON area */
+// uint64_t seqid; /* increased on every update */
+// char label[LUKS2_LABEL_L];
+// char checksum_alg[LUKS2_CHECKSUM_ALG_L];
+// uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
+// char uuid[LUKS2_UUID_L];
+// char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
+// uint64_t hdr_offset; /* offset from device start in bytes */
+// char _padding[184];
+// uint8_t csum[LUKS2_CHECKSUM_L];
+// }
+message LUKS2_header {
+ required luks_version version = 1;
+ required luks2_magic magic = 2;
+ required hdr_size hdr_size = 3;
+ required bool use_correct_checksum = 4;
+
+ optional uint64 selected_offset = 5;
+}
+
+message LUKS2_both_headers {
+ required LUKS2_header primary_header = 1;
+ required LUKS2_header secondary_header = 2;
+
+ required seqid_description seqid = 3;
+ required json_area_description json_area = 4;
+}
+
+message json_area_description {
+ optional config_description config = 1;
+ repeated keyslot_description keyslots = 2;
+ repeated digest_description digests = 3;
+ repeated segment_description segments = 4;
+ repeated token_description tokens = 5;
+}
+
+// ---------------------------------------------------------------------------
+// ----------------------------- KEYSLOT OBJECT ------------------------------
+// ---------------------------------------------------------------------------
+
+enum keyslot_type {
+ KEYSLOT_TYPE_LUKS2 = 1;
+ KEYSLOT_TYPE_REENCRYPT = 2;
+ KEYSLOT_TYPE_PLACEHOLDER = 3;
+}
+
+enum reencrypt_keyslot_mode {
+ MODE_REENCRYPT = 1;
+ MODE_ENCRYPT = 2;
+ MODE_DECRYPT = 3;
+}
+
+enum reencrypt_keyslot_direction {
+ DIRECTION_FORWARD = 1;
+ DIRECTION_BACKWARD = 2;
+}
+
+// The area object contains these mandatory fields:
+// - type [string] the area type.
+// - offset [string-uint64] the offset from the device start to the beginning of the binary area (in bytes).
+// - size [string-uint64] the area size (in bytes).
+//
+// Area type raw contains these additional fields:
+// - encryption [string] the area encryption algorithm, in dm-crypt notation (for example aes-xts-plain64).
+// - key_size [integer] the area encryption key size.
+//
+// Area type none and journal (used only for reencryption optional extension) contain only mandatory fields.
+//
+// Area type checksum (used only for reencryption optional extension) contains these additional fields:
+// - hash [string] The hash algorithm for the checksum resilience mode.
+// - sector_size [integer] The data unit size for digest checksum calculated with the hash algorithm.
+//
+// Area type datashift (used only for reencryption optional extension) contains this additional field:
+// - shift_size [string-uint64] The data shift (in bytes) performed during reencryption (shift direction is according to direction field).
+
+enum keyslot_area_type {
+ KEYSLOT_AREA_TYPE_RAW = 1;
+ KEYSLOT_AREA_TYPE_NONE = 2;
+ KEYSLOT_AREA_TYPE_JOURNAL = 3;
+ KEYSLOT_AREA_TYPE_CHECKSUM = 4;
+ KEYSLOT_AREA_TYPE_DATASHIFT = 5;
+}
+
+message keyslot_area_description {
+ // mandatory fields
+ optional keyslot_area_type type = 1;
+ optional string_uint64 offset = 2;
+ optional string_uint64 size = 3;
+
+ // raw type fields
+ optional string encryption = 4;
+ optional int32 key_size = 5;
+
+ // checksum type field
+ optional hash_algorithm hash = 6;
+ optional int32 sector_size = 7;
+
+ // datashift type fields
+ optional string_uint64 shift_size = 8;
+}
+
+// The object describes PBKDF attributes used for the keyslot.
+// The kdf object mandatory fields are:
+// - type [string] the PBKDF type.
+// - salt [base64] the salt for PBKDF (binary data).
+//
+// The pbkdf2 type (compatible with LUKS1) contains these additional fields:
+// - hash [string] the hash algorithm for the PBKDF2 (SHA-256).
+// - iterations [integer] the PBKDF2 iterations count.
+//
+// The argon2i and argon2id type contains these additional fields:
+// - time [integer] the time cost (in fact the iterations count for Argon2).
+// - memory [integer] the memory cost, in kilobytes. If not available, the keyslot cannot be unlocked.
+// - cpus [integer] the required number of threads (CPU cores number cost). If not available, unlocking will be slower.
+
+enum keyslot_kdf_type {
+ KEYSLOT_KDF_TYPE_PBKDF2 = 1;
+ KEYSLOT_KDF_TYPE_ARGON2I = 2;
+ KEYSLOT_KDF_TYPE_ARGON2ID = 3;
+}
+
+message keyslot_kdf_description {
+ optional keyslot_kdf_type type = 1;
+ optional string salt = 2;
+
+ // pbkdf2 type
+ optional hash_algorithm hash = 3;
+ optional int32 iterations = 4;
+
+ // argon2i and argon2id types
+ optional int32 time = 5;
+ optional int32 memory = 6;
+ optional int32 cpus = 7;
+}
+
+enum keyslot_af_type {
+ KEYSLOT_AF_TYPE_LUKS1 = 1;
+}
+
+// The af (anti-forensic splitter) object contains this madatory field:
+// - type [string] the anti-forensic function type.
+// AF type luks1 (compatible with LUKS1 [1]) contains these additional fields:
+// - stripes [integer] the number of stripes, for historical reasons only the 4000 value is supported.
+// - hash [string] the hash algorithm used.
+
+message keyslot_af_description {
+ optional keyslot_af_type type = 1;
+ optional int32 stripes = 2;
+ optional hash_algorithm hash = 3;
+}
+
+// - type [string] the keyslot type.
+// - key_size [integer] the key size (in bytes) stored in keyslot.
+// - priority [integer,optional] the keyslot priority. Here 0 means ignore (the slot should be used only if explicitly stated), 1 means normal priority and 2 means high priority (tried before normal priority).
+
+// REENCRYPT
+// The key size field must be set to 1. The area type must be none, checksum,
+// journal or datashift.
+// The reencrypt object must contain these additional fields:
+// - mode [string] the reencryption mode. reencrypt, encrypt and decrypt
+// - direction [string] the reencryption direction. forward backward
+
+// - area [object] the allocated area in the binary keyslots area.
+// LUKS2 object must contain these additional fields:
+// - kdf [object] the PBKDF type and parameters used.
+// - af [object] the anti-forensic splitter [1] (only the luks1 type is currently
+// used).
+
+message keyslot_description {
+ // type
+ required object_id oid = 1;
+
+ optional keyslot_type type = 2;
+ optional int32 key_size = 3;
+ optional int32 priority = 4;
+
+ // reencrypt extension
+ optional reencrypt_keyslot_mode mode = 5;
+ optional reencrypt_keyslot_direction direction = 6;
+
+ // objects
+ optional keyslot_area_description area = 7;
+ optional keyslot_kdf_description kdf = 8;
+ optional keyslot_af_description af = 9;
+}
+
+// ---------------------------------------------------------------------------
+// ------------------------------ DIGEST OBJECT ------------------------------
+// ---------------------------------------------------------------------------
+
+message digest_description {
+ required object_id oid = 1;
+
+ optional keyslot_kdf_type type = 2;
+ repeated object_id keyslots = 3;
+ repeated object_id segments = 4;
+ optional string salt = 5;
+ optional string digest = 6;
+
+ // pbkdf2 digest fields
+ optional hash_algorithm hash = 7;
+ optional int32 iterations = 8;
+}
+
+// ---------------------------------------------------------------------------
+// ----------------------------- SEGMENT OBJECT ------------------------------
+// ---------------------------------------------------------------------------
+
+enum segment_type {
+ SEGMENT_TYPE_LINEAR = 1;
+ SEGMENT_TYPE_CRYPT = 2;
+}
+
+enum segment_flag {
+ IN_REENCRYPTION = 1;
+ BACKUP_FINAL = 2;
+ BACKUP_PREVIOUS = 3;
+ BACKUP_MOVED_SEGMENT = 4;
+}
+
+message segment_integrity_description {
+ optional string type = 1;
+ optional string journal_encryption = 2;
+ optional string journal_integrity = 3;
+}
+
+message segment_description {
+ required object_id oid = 1;
+ optional segment_type type = 2;
+ optional string_uint64 offset = 3;
+ optional string_uint64 size = 4;
+ repeated segment_flag flags = 5;
+
+ // segment type crypt
+ optional string_uint64 iv_tweak = 6;
+ optional string encryption = 7;
+ optional int32 sector_size = 8;
+ optional segment_integrity_description integrity = 9;
+}
+
+// ---------------------------------------------------------------------------
+// ------------------------------ TOKEN OBJECT -------------------------------
+// ---------------------------------------------------------------------------
+
+message token_description {
+ required object_id oid = 1;
+
+ optional string type = 2;
+ repeated object_id keyslots = 3;
+ optional string key_description = 4;
+}
+
+// ---------------------------------------------------------------------------
+// ------------------------------ CONFIG OBJECT ------------------------------
+// ---------------------------------------------------------------------------
+
+// - allow-discards allows TRIM (discards) on the active device.
+// - same-cpu-crypt compatibility performance flag for dm-crypt [3] to per- form encryption using the same CPU that originated the request.
+// - submit-from-crypt-cpus compatibility performance flag for dm-crypt [3] to disable offloading write requests to a separate thread after encryption.
+// - no-journal disable data journalling for dm-integrity [10].
+// - no-read-workqueue compatibility performance flag for dm-crypt [3] to bypass dm-crypt read workqueue and process read requests synchronously.
+// - no-write-workqueue compatibility performance flag for dm-crypt [3] to bypass dm-crypt write workqueue and process write requests synchronously.
+enum config_flag {
+ CONFIG_FLAG_ALLOW_DISCARDS = 1;
+ CONFIG_FLAG_SAME_CPU_CRYPT = 2;
+ CONFIG_FLAG_SUBMIT_FROM_CRYPT_CPUS = 3;
+ CONFIG_FLAG_NO_JOURNAL = 4;
+ CONFIG_FLAG_NO_READ_WORKQUEUE = 5;
+ CONFIG_FLAG_NO_WRITE_WORKQUEUE = 6;
+}
+
+enum config_requirement {
+ CONFIG_REQUIREMENT_OFFLINE_REENCRYPT = 1;
+ CONFIG_REQUIREMENT_ONLINE_REENCRYPT_V2 = 2;
+}
+
+// - json_size [string-uint64] the JSON area size (in bytes). Must match the binary header.
+// - keyslots_size [string-uint64] the binary keyslot area size (in bytes). Must be aligned to 4096 bytes.
+// - flags [array, optional] the array of string objects with persistent flags for the device.
+// - requirements [array, optional] the array of string objects with additional required features for the LUKS device.
+
+message config_description {
+ required bool use_primary_hdr_size = 2;
+
+ repeated config_flag config_flags = 3;
+ repeated config_requirement requirements = 4;
+}
diff --git a/tests/fuzz/LUKS2_plain_JSON.proto b/tests/fuzz/LUKS2_plain_JSON.proto
new file mode 100644
index 0000000..59096b7
--- /dev/null
+++ b/tests/fuzz/LUKS2_plain_JSON.proto
@@ -0,0 +1,190 @@
+/*
+ * cryptsetup LUKS2 custom mutator
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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.
+ */
+
+syntax = "proto2";
+
+package json_proto;
+
+// ---------------------------------------------------------------------------
+// ----------------------------- GENERIC OBJECTS -----------------------------
+// ---------------------------------------------------------------------------
+
+message object_id {
+ oneof id {
+ // int_id will be mapped to range -16 to 16 (mod 33)
+ // this way iy should be easier to generate valid
+ // object cross-references
+ uint32 int_id = 1;
+ string string_id = 2;
+ }
+}
+
+message string_uint64 {
+ required bool negative = 1;
+ oneof number {
+ uint32 uint_num = 2;
+ string string_num = 3;
+ }
+}
+
+enum hash_algorithm {
+ HASH_ALG_SHA1 = 1;
+ HASH_ALG_SHA256 = 2;
+}
+
+
+// ---------------------------------------------------------------------------
+// ----------------------------- BINARY HEADER -------------------------------
+// ---------------------------------------------------------------------------
+
+enum luks2_magic {
+ INVALID = 0;
+ FIRST = 1;
+ SECOND = 2;
+}
+
+enum luks_version {
+ ONE = 1;
+ TWO = 2;
+ THREE = 3;
+}
+
+// we limit the size to 64KiB to make the fuzzing faster
+// because the checksum needs to be calculated for the whole image
+enum hdr_size {
+ size_16_KB = 16384;
+ size_32_KB = 32768;
+ size_64_KB = 65536;
+// size_128_KB = 131072;
+// size_256_KB = 262144;
+// size_512_KB = 524288;
+// size_1_MB = 1048576;
+// size_2_MB = 2097152;
+// size_4_MB = 4194304;
+}
+
+enum seqid_description {
+ PRIMARY_GREATER = 0;
+ SECONDARY_GREATER = 1;
+ EQUAL = 2;
+}
+
+// message luks2_hdr_disk {
+// char magic[LUKS2_MAGIC_L];
+// //uint16_t version; /* Version 2 */
+// uint64_t hdr_size; /* in bytes, including JSON area */
+// uint64_t seqid; /* increased on every update */
+// char label[LUKS2_LABEL_L];
+// char checksum_alg[LUKS2_CHECKSUM_ALG_L];
+// uint8_t salt[LUKS2_SALT_L]; /* unique for every header/offset */
+// char uuid[LUKS2_UUID_L];
+// char subsystem[LUKS2_LABEL_L]; /* owner subsystem label */
+// uint64_t hdr_offset; /* offset from device start in bytes */
+// char _padding[184];
+// uint8_t csum[LUKS2_CHECKSUM_L];
+// }
+message LUKS2_header {
+ required luks_version version = 1;
+ required luks2_magic magic = 2;
+ required hdr_size hdr_size = 3;
+ required bool use_correct_checksum = 4;
+
+ optional uint64 selected_offset = 5;
+}
+
+message LUKS2_both_headers {
+ required LUKS2_header primary_header = 1;
+ required LUKS2_header secondary_header = 2;
+
+ required seqid_description seqid = 3;
+ required JsonObject json_area = 4;
+}
+
+message JsonObject {
+ required string name = 1;
+ required JsonValue value = 2;
+}
+
+message JsonValue {
+ oneof value {
+ // Json value types:
+
+ // null: null, will be used when 'oneof' contains nothing
+
+ // object: another json object of any type
+ JsonObject object_value = 1;
+
+ // array: an array of values
+ ArrayValue array_value = 2;
+
+ // number: can be an integer, a float, an exponent
+ NumberValue number_value = 3;
+
+ // string: unicode string
+ StringValue string_value = 4;
+
+ // boolean: true or talse
+ BooleanValue boolean_value = 5;
+ }
+}
+
+message ArrayValue {
+ repeated JsonValue value = 1;
+}
+
+message NumberInteger {
+ required int64 value = 1;
+}
+
+message NumberFloat {
+ required double value = 1;
+}
+
+message NumberExponent {
+ required int32 base = 1;
+ required int32 exponent = 2;
+ required bool use_uppercase = 3;
+}
+
+message NumberExponentFrac {
+ required float base = 1;
+ required int32 exponent = 2;
+ required bool use_uppercase = 3;
+}
+
+message NumberValue {
+ required NumberInteger integer_value = 1;
+
+ // integer_value is used when oneof field below has nothing.
+ oneof value {
+ NumberFloat float_value = 2;
+ NumberExponent exponent_value = 3;
+ NumberExponentFrac exponent_frac_value = 4;
+ }
+}
+
+message StringValue {
+ required string value = 1;
+}
+
+message BooleanValue {
+ required bool value = 1;
+}
diff --git a/tests/fuzz/Makefile.am b/tests/fuzz/Makefile.am
new file mode 100644
index 0000000..c7a6cdf
--- /dev/null
+++ b/tests/fuzz/Makefile.am
@@ -0,0 +1,122 @@
+EXTRA_DIST = README.md oss-fuzz-build.sh
+dist_noinst_DATA = \
+ LUKS2.proto \
+ LUKS2_plain_JSON.proto \
+ crypt2_load_fuzz.dict \
+ crypt2_load_ondisk_fuzz.dict \
+ crypt2_load_proto_plain_json_fuzz.dict \
+ unpoison-mutated-buffers-from-libfuzzer.patch
+CLEANFILES = \
+ LUKS2.pb.h \
+ LUKS2.pb.cc \
+ LUKS2_plain_JSON.pb.h \
+ LUKS2_plain_JSON.pb.cc
+
+distclean-local:
+ -rm -rf out build
+
+LIB_FUZZING_ENGINE := $(if $(LIB_FUZZING_ENGINE),$(LIB_FUZZING_ENGINE),"-fsanitize=fuzzer")
+SANITIZER := $(if $(SANITIZER),,"-fsanitize=address")
+
+DEPS_PATH := $(top_srcdir)/tests/fuzz/build/static_lib_deps
+
+crypt2_load_fuzz_SOURCES = FuzzerInterface.h crypt2_load_fuzz.cc
+crypt2_load_fuzz_LDADD = ../../libcryptsetup.la ../../libcrypto_backend.la -L$(DEPS_PATH)/lib
+crypt2_load_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) $(SANITIZER)
+crypt2_load_fuzz_CXXFLAGS = $(AM_CXXFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz
+
+crypt2_load_ondisk_fuzz_SOURCES = FuzzerInterface.h crypt2_load_ondisk_fuzz.cc
+crypt2_load_ondisk_fuzz_LDADD = ../../libcryptsetup.la -L$(DEPS_PATH)/lib
+crypt2_load_ondisk_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) $(SANITIZER)
+crypt2_load_ondisk_fuzz_CXXFLAGS = $(AM_CXXFLAGS) -I$(top_srcdir)/lib -I$(top_srcdir)/tests/fuzz
+
+test-environment-m:
+ @ if test ! -d $(DEPS_PATH); then \
+ echo "You need to build static libraries first; use oss-fuzz-build.sh script."; \
+ exit 1; \
+ fi
+test-environment: | test-environment-m $(DEPS_PATH)
+
+LUKS2.pb.h: LUKS2.proto
+ $(DEPS_PATH)/bin/protoc LUKS2.proto --cpp_out=.
+LUKS2.pb.cc: LUKS2.pb.h
+
+LUKS2_plain_JSON.pb.h: LUKS2_plain_JSON.proto
+ $(DEPS_PATH)/bin/protoc LUKS2_plain_JSON.proto --cpp_out=.
+LUKS2_plain_JSON.pb.cc: LUKS2_plain_JSON.pb.h
+
+crypt2_load_proto_fuzz-crypt2_load_proto_fuzz.$(OBJEXT): LUKS2.pb.cc
+crypt2_load_proto_plain_json_fuzz-crypt2_load_proto_plain_json_fuzz.$(OBJEXT): LUKS2_plain_JSON.pb.cc
+
+nodist_crypt2_load_proto_fuzz_SOURCES = LUKS2.pb.h LUKS2.pb.cc
+crypt2_load_proto_fuzz_SOURCES = FuzzerInterface.h \
+ crypt2_load_proto_fuzz.cc \
+ proto_to_luks2_converter.h \
+ proto_to_luks2_converter.cc
+crypt2_load_proto_fuzz_LDADD = \
+ ../../libcryptsetup.la \
+ ../../libcrypto_backend.la \
+ -L$(DEPS_PATH)/lib -lprotobuf-mutator-libfuzzer -lprotobuf-mutator -lprotobuf
+crypt2_load_proto_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) $(SANITIZER)
+crypt2_load_proto_fuzz_CXXFLAGS = $(AM_CXXFLAGS) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/tests/fuzz \
+ -I$(DEPS_PATH)/include \
+ -I$(DEPS_PATH)/include/libprotobuf-mutator -I$(DEPS_PATH)/include/libprotobuf-mutator/src
+
+nodist_crypt2_load_proto_plain_json_fuzz_SOURCES = LUKS2_plain_JSON.pb.h LUKS2_plain_JSON.pb.cc
+crypt2_load_proto_plain_json_fuzz_SOURCES = FuzzerInterface.h \
+ crypt2_load_proto_plain_json_fuzz.cc \
+ json_proto_converter.h \
+ json_proto_converter.cc \
+ plain_json_proto_to_luks2_converter.h \
+ plain_json_proto_to_luks2_converter.cc
+crypt2_load_proto_plain_json_fuzz_LDADD = \
+ ../../libcryptsetup.la \
+ ../../libcrypto_backend.la \
+ -L$(DEPS_PATH)/lib -lprotobuf-mutator-libfuzzer -lprotobuf-mutator -lprotobuf
+crypt2_load_proto_plain_json_fuzz_LDFLAGS = $(AM_LDFLAGS) $(LIB_FUZZING_ENGINE) $(SANITIZER)
+crypt2_load_proto_plain_json_fuzz_CXXFLAGS = $(AM_CXXFLAGS) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/tests/fuzz \
+ -I$(DEPS_PATH)/include \
+ -I$(DEPS_PATH)/include/libprotobuf-mutator -I$(DEPS_PATH)/include/libprotobuf-mutator/src
+
+nodist_proto_to_luks2_SOURCES = LUKS2.pb.h LUKS2.pb.cc
+proto_to_luks2_SOURCES = \
+ proto_to_luks2.cc \
+ proto_to_luks2_converter.h \
+ proto_to_luks2_converter.cc
+proto_to_luks2_LDADD = ../../libcryptsetup.la ../../libcrypto_backend.la -L$(DEPS_PATH)/lib -lprotobuf
+proto_to_luks2_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer-no-link $(SANITIZER)
+proto_to_luks2_CXXFLAGS = $(AM_CXXFLAGS) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/tests/fuzz \
+ -I$(DEPS_PATH)/include
+
+nodist_plain_json_proto_to_luks2_SOURCES = LUKS2_plain_JSON.pb.h LUKS2_plain_JSON.pb.cc
+plain_json_proto_to_luks2_SOURCES = \
+ plain_json_proto_to_luks2.cc \
+ plain_json_proto_to_luks2_converter.h \
+ plain_json_proto_to_luks2_converter.cc \
+ json_proto_converter.h \
+ json_proto_converter.cc
+plain_json_proto_to_luks2_LDADD = ../../libcryptsetup.la ../../libcrypto_backend.la -L$(DEPS_PATH)/lib -lprotobuf
+plain_json_proto_to_luks2_LDFLAGS = $(AM_LDFLAGS) -fsanitize=fuzzer-no-link $(SANITIZER)
+plain_json_proto_to_luks2_CXXFLAGS = $(AM_CXXFLAGS) \
+ -I$(top_srcdir)/lib \
+ -I$(top_srcdir)/tests/fuzz \
+ -I$(DEPS_PATH)/include
+
+if ENABLE_FUZZ_TARGETS
+noinst_PROGRAMS = \
+ crypt2_load_fuzz \
+ crypt2_load_ondisk_fuzz \
+ crypt2_load_proto_fuzz \
+ crypt2_load_proto_plain_json_fuzz \
+ proto_to_luks2 \
+ plain_json_proto_to_luks2
+
+fuzz-targets: test-environment $(noinst_PROGRAMS)
+.PHONY: fuzz-targets
+endif
diff --git a/tests/fuzz/README.md b/tests/fuzz/README.md
new file mode 100644
index 0000000..fdcfa27
--- /dev/null
+++ b/tests/fuzz/README.md
@@ -0,0 +1,66 @@
+# Fuzzing target for cryptsetup project
+
+This directory contains experimental targets for fuzzing testing.
+It can be run in the OSS-Fuzz project but also compiled separately.
+
+# Requirements
+
+Fuzzers use address sanitizer. To properly detect problems, all
+important libraries must be compiled statically with sanitizer enabled.
+
+Compilation requires *clang* and *clang++* compilers (gcc is not
+supported yet).
+
+# Standalone build
+
+The script `oss-fuzz-build.sh` can be used to prepare the tree
+with pre-compiled library dependencies.
+We use upstream git for projects, which can clash with locally
+installed versions. The best is to use only basic system installation
+without development packages (script will use custom include, libs,
+and pkg-config paths).
+
+# Build Docker image and fuzzers
+
+You can also run OSS-Fuzz in a Docker image, use these commands
+to prepare fuzzers:
+```
+sudo python3 infra/helper.py build_image cryptsetup
+sudo python3 infra/helper.py build_fuzzers cryptsetup
+```
+On SELinux systems also add (https://github.com/google/oss-fuzz/issues/30):
+```
+sudo chcon -Rt svirt_sandbox_file_t build/
+```
+
+# Run LUKS2 fuzzer
+`FUZZER_NAME` can be one of: `crypt2_load_fuzz`, `crypt2_load_proto_fuzz`, `crypt2_load_proto_plain_json_fuzz`
+```
+FUZZER_NAME="crypt2_load_proto_plain_json_fuzz"
+sudo mkdir -p build/corpus/cryptsetup/$FUZZER_NAME
+sudo python infra/helper.py run_fuzzer --corpus-dir build/corpus/cryptsetup/$FUZZER_NAME/ --sanitizer address cryptsetup $FUZZER_NAME '-jobs=8 -workers=8'
+```
+
+The output of the parallel threads will be written to `fuzz-<N>.log` (where `<N>` is the number of the process).
+You can watch it using e.g.:
+```
+tail -f build/out/cryptsetup/fuzz-*
+```
+
+Optionally, you can use experimental `fork` mode for parallelization and the output will be displayed directly on the terminal:
+```
+sudo python infra/helper.py run_fuzzer --corpus-dir build/corpus/cryptsetup/$FUZZER_NAME/ --sanitizer address cryptsetup $FUZZER_NAME '-fork=8 '
+```
+
+# Rebuild fuzz targets for coverage
+```
+sudo python infra/helper.py build_fuzzers --sanitizer coverage cryptsetup
+```
+
+# Generate coverage report
+```
+sudo python infra/helper.py coverage cryptsetup --no-corpus-download --fuzz-target $FUZZER_NAME
+```
+
+# Further information
+For more details, you can look into the [Using fuzzing for Linux disk encryption tools](https://is.muni.cz/th/bum03/?lang=en) thesis.
diff --git a/tests/fuzz/crypt2_load_fuzz.cc b/tests/fuzz/crypt2_load_fuzz.cc
new file mode 100644
index 0000000..1251d72
--- /dev/null
+++ b/tests/fuzz/crypt2_load_fuzz.cc
@@ -0,0 +1,112 @@
+/*
+ * cryptsetup LUKS2 fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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.
+ */
+
+extern "C" {
+#define FILESIZE (16777216)
+#include "src/cryptsetup.h"
+#include <err.h>
+#include "luks2/luks2.h"
+#include "crypto_backend/crypto_backend.h"
+#include "FuzzerInterface.h"
+
+static int calculate_checksum(const uint8_t* data, size_t size) {
+ struct crypt_hash *hd = NULL;
+ struct luks2_hdr_disk *hdr = NULL;
+ int hash_size;
+ uint64_t hdr_size1, hdr_size2;
+ int r = 0;
+
+ /* primary header */
+ if (sizeof(struct luks2_hdr_disk) > size)
+ return 0;
+ hdr = CONST_CAST(struct luks2_hdr_disk *) data;
+
+ hdr_size1 = be64_to_cpu(hdr->hdr_size);
+ if (hdr_size1 > size)
+ return 0;
+ memset(&hdr->csum, 0, LUKS2_CHECKSUM_L);
+ if ((r = crypt_hash_init(&hd, "sha256")))
+ goto out;
+ if ((r = crypt_hash_write(hd, CONST_CAST(char*) data, hdr_size1)))
+ goto out;
+ hash_size = crypt_hash_size("sha256");
+ if (hash_size <= 0) {
+ r = 1;
+ goto out;
+ }
+ if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size)))
+ goto out;
+ crypt_hash_destroy(hd);
+
+ /* secondary header */
+ if (hdr_size1 < sizeof(struct luks2_hdr_disk))
+ hdr_size1 = sizeof(struct luks2_hdr_disk);
+
+ if (hdr_size1 + sizeof(struct luks2_hdr_disk) > size)
+ return 0;
+ hdr = CONST_CAST(struct luks2_hdr_disk *) (data + hdr_size1);
+
+ hdr_size2 = be64_to_cpu(hdr->hdr_size);
+ if (hdr_size2 > size || (hdr_size1 + hdr_size2) > size)
+ return 0;
+
+ memset(&hdr->csum, 0, LUKS2_CHECKSUM_L);
+ if ((r = crypt_hash_init(&hd, "sha256")))
+ goto out;
+ if ((r = crypt_hash_write(hd, (char*) hdr, hdr_size2)))
+ goto out;
+ if ((r = crypt_hash_final(hd, (char*)&hdr->csum, (size_t)hash_size)))
+ goto out;
+
+out:
+ if (hd)
+ crypt_hash_destroy(hd);
+ return r;
+}
+
+int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ int fd;
+ struct crypt_device *cd = NULL;
+ char name[] = "/tmp/test-script-fuzz.XXXXXX";
+
+ if (calculate_checksum(data, size))
+ return 0;
+
+ fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
+ if (fd == -1)
+ err(EXIT_FAILURE, "mkostemp() failed");
+
+ /* enlarge header */
+ if (ftruncate(fd, FILESIZE) == -1)
+ goto out;
+
+ if (write_buffer(fd, data, size) != (ssize_t)size)
+ goto out;
+
+ if (crypt_init(&cd, name) == 0)
+ (void)crypt_load(cd, CRYPT_LUKS2, NULL);
+ crypt_free(cd);
+out:
+ close(fd);
+ unlink(name);
+ return 0;
+}
+}
diff --git a/tests/fuzz/crypt2_load_fuzz.dict b/tests/fuzz/crypt2_load_fuzz.dict
new file mode 100644
index 0000000..fedf1a4
--- /dev/null
+++ b/tests/fuzz/crypt2_load_fuzz.dict
@@ -0,0 +1,130 @@
+# LUKS2 dictionary based on AFL dictionary for JSON
+# -------------------------------------------------
+# JSON dictionary from https://github.com/google/AFL/blob/master/dictionaries/json.dict
+# Inspired by a dictionary by Jakub Wilk <jwilk@jwilk.net>
+#
+# LUKS2 keywords by Daniel Zatovic
+
+"0"
+",0"
+":0"
+"0:"
+"-1.2e+3"
+
+"true"
+"false"
+"null"
+
+"\"\""
+",\"\""
+":\"\""
+"\"\":"
+
+"{}"
+",{}"
+":{}"
+"{\"\":0}"
+"{{}}"
+
+"[]"
+",[]"
+":[]"
+"[0]"
+"[[]]"
+
+"''"
+"\\"
+"\\b"
+"\\f"
+"\\n"
+"\\r"
+"\\t"
+"\\u0000"
+"\\x00"
+"\\0"
+"\\uD800\\uDC00"
+"\\uDBFF\\uDFFF"
+
+"\"\":0"
+"//"
+"/**/"
+
+"$ref"
+"type"
+"coordinates"
+"@context"
+"@id"
+
+","
+":"
+
+"1024"
+"2048"
+"4096"
+"512"
+"aegis128-random"
+"aes-cbc:essiv:sha256"
+"aes-xts-plain64"
+"af"
+"allow-discards"
+"area"
+"argon2i"
+"argon2id"
+"backup-final"
+"backup-moved-segment"
+"backup-previous"
+"checksum"
+"config"
+"cpus"
+"crypt"
+"datashift"
+"digest"
+"digests"
+"direction"
+"encryption"
+"flags"
+"hash"
+"in-reencryption"
+"integrity"
+"iterations"
+"iv_tweak"
+"journal"
+"journal_encryption"
+"journal_integrity"
+"json_size"
+"kdf"
+"key_description"
+"key_size"
+"keyslots"
+"keyslots_size"
+"linear"
+"luks2"
+"luks2-keyring"
+"LUKS\xBA\xBE"
+"memory"
+"mode"
+"no-journal"
+"none"
+"no-read-workqueue"
+"no-write-workqueue"
+"offline-reencrypt"
+"offset"
+"online-reencrypt-v2"
+"pbkdf2"
+"priority"
+"raw"
+"reencrypt"
+"requirements"
+"salt"
+"same-cpu-crypt"
+"sector_size"
+"segments"
+"serpent-xts-plain64"
+"shift_size"
+"size"
+"SKUL\xBA\xBE"
+"stripes"
+"submit-from-crypt-cpus"
+"time"
+"tokens"
+"twofish-xts-plain64"
diff --git a/tests/fuzz/crypt2_load_ondisk_fuzz.cc b/tests/fuzz/crypt2_load_ondisk_fuzz.cc
new file mode 100644
index 0000000..9b5328d
--- /dev/null
+++ b/tests/fuzz/crypt2_load_ondisk_fuzz.cc
@@ -0,0 +1,64 @@
+/*
+ * cryptsetup LUKS1, FileVault, BitLocker fuzz target
+ *
+ * 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.
+ */
+
+extern "C" {
+#define FILESIZE (16777216)
+#include "src/cryptsetup.h"
+#include <err.h>
+#include "luks1/luks.h"
+#include "crypto_backend/crypto_backend.h"
+#include "FuzzerInterface.h"
+
+void empty_log(int level, const char *msg, void *usrptr) {}
+
+int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ int fd, r;
+ struct crypt_device *cd = NULL;
+ char name[] = "/tmp/test-script-fuzz.XXXXXX";
+
+ fd = mkostemp(name, O_RDWR | O_CREAT | O_EXCL | O_CLOEXEC);
+ if (fd == -1)
+ err(EXIT_FAILURE, "mkostemp() failed");
+
+ /* enlarge header */
+ if (ftruncate(fd, FILESIZE) == -1)
+ goto out;
+
+ if (write_buffer(fd, data, size) != (ssize_t) size)
+ goto out;
+
+ crypt_set_log_callback(NULL, empty_log, NULL);
+
+ if (crypt_init(&cd, name) == 0) {
+ r = crypt_load(cd, CRYPT_LUKS1, NULL);
+ if (r == 0)
+ goto out;
+
+ r = crypt_load(cd, CRYPT_FVAULT2, NULL);
+ if (r == 0)
+ goto out;
+
+ (void) crypt_load(cd, CRYPT_BITLK, NULL);
+ }
+out:
+ crypt_free(cd);
+ close(fd);
+ unlink(name);
+ return 0;
+}
+}
diff --git a/tests/fuzz/crypt2_load_ondisk_fuzz.dict b/tests/fuzz/crypt2_load_ondisk_fuzz.dict
new file mode 100644
index 0000000..3923db5
--- /dev/null
+++ b/tests/fuzz/crypt2_load_ondisk_fuzz.dict
@@ -0,0 +1,9 @@
+"aegis128-random"
+"aes-cbc:essiv:sha256"
+"aes-xts-plain64"
+"aes-lrv-plain64"
+"twofish-xts-plain64"
+"serpent-xts-plain64"
+"whirpool"
+"sha256"
+"sha1"
diff --git a/tests/fuzz/crypt2_load_proto_fuzz.cc b/tests/fuzz/crypt2_load_proto_fuzz.cc
new file mode 100644
index 0000000..498c006
--- /dev/null
+++ b/tests/fuzz/crypt2_load_proto_fuzz.cc
@@ -0,0 +1,51 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 "LUKS2.pb.h"
+#include "proto_to_luks2_converter.h"
+#include "libfuzzer/libfuzzer_macro.h"
+#include "FuzzerInterface.h"
+
+extern "C" {
+#include <libcryptsetup.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+DEFINE_PROTO_FUZZER(const LUKS2_proto::LUKS2_both_headers &headers) {
+ struct crypt_device *cd = NULL;
+ char name[] = "/tmp/test-proto-fuzz.XXXXXX";
+ int fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
+
+ if (fd < 0)
+ err(EXIT_FAILURE, "mkostemp() failed");
+
+ LUKS2_proto::LUKS2ProtoConverter converter;
+ converter.convert(headers, fd);
+
+ if (crypt_init(&cd, name) == 0)
+ (void)crypt_load(cd, CRYPT_LUKS2, NULL);
+ crypt_free(cd);
+
+ close(fd);
+ unlink(name);
+}
diff --git a/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc
new file mode 100644
index 0000000..f3565ab
--- /dev/null
+++ b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.cc
@@ -0,0 +1,51 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 "LUKS2_plain_JSON.pb.h"
+#include "plain_json_proto_to_luks2_converter.h"
+#include "libfuzzer/libfuzzer_macro.h"
+#include "FuzzerInterface.h"
+
+extern "C" {
+#include <libcryptsetup.h>
+#include <err.h>
+#include <fcntl.h>
+#include <unistd.h>
+}
+
+DEFINE_PROTO_FUZZER(const json_proto::LUKS2_both_headers &headers) {
+ struct crypt_device *cd = NULL;
+ char name[] = "/tmp/test-proto-fuzz.XXXXXX";
+ int fd = mkostemp(name, O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC);
+
+ if (fd < 0)
+ err(EXIT_FAILURE, "mkostemp() failed");
+
+ json_proto::LUKS2ProtoConverter converter;
+ converter.convert(headers, fd);
+
+ if (crypt_init(&cd, name) == 0)
+ (void)crypt_load(cd, CRYPT_LUKS2, NULL);
+ crypt_free(cd);
+
+ close(fd);
+ unlink(name);
+}
diff --git a/tests/fuzz/crypt2_load_proto_plain_json_fuzz.dict b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.dict
new file mode 100644
index 0000000..7d83151
--- /dev/null
+++ b/tests/fuzz/crypt2_load_proto_plain_json_fuzz.dict
@@ -0,0 +1,72 @@
+# LUKS2 keywords by Daniel Zatovic
+
+"1024"
+"2048"
+"4096"
+"512"
+"aegis128-random"
+"aes-cbc:essiv:sha256"
+"aes-xts-plain64"
+"af"
+"allow-discards"
+"area"
+"argon2i"
+"argon2id"
+"backup-final"
+"backup-moved-segment"
+"backup-previous"
+"checksum"
+"config"
+"cpus"
+"crypt"
+"datashift"
+"digest"
+"digests"
+"direction"
+"encryption"
+"flags"
+"hash"
+"in-reencryption"
+"integrity"
+"iterations"
+"iv_tweak"
+"journal"
+"journal_encryption"
+"journal_integrity"
+"json_size"
+"kdf"
+"key_description"
+"key_size"
+"keyslots"
+"keyslots_size"
+"linear"
+"luks2"
+"luks2-keyring"
+"LUKS\xBA\xBE"
+"memory"
+"mode"
+"no-journal"
+"none"
+"no-read-workqueue"
+"no-write-workqueue"
+"offline-reencrypt"
+"offset"
+"online-reencrypt-v2"
+"pbkdf2"
+"priority"
+"raw"
+"reencrypt"
+"requirements"
+"salt"
+"same-cpu-crypt"
+"sector_size"
+"segments"
+"serpent-xts-plain64"
+"shift_size"
+"size"
+"SKUL\xBA\xBE"
+"stripes"
+"submit-from-crypt-cpus"
+"time"
+"tokens"
+"twofish-xts-plain64"
diff --git a/tests/fuzz/json_proto_converter.cc b/tests/fuzz/json_proto_converter.cc
new file mode 100644
index 0000000..ed453be
--- /dev/null
+++ b/tests/fuzz/json_proto_converter.cc
@@ -0,0 +1,87 @@
+// Copyright 2020 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#include "json_proto_converter.h"
+
+namespace json_proto {
+
+void JsonProtoConverter::AppendArray(const ArrayValue& array_value) {
+ data_ << '[';
+ bool need_comma = false;
+ for (const auto& value : array_value.value()) {
+ // Trailing comma inside of an array makes JSON invalid, avoid adding that.
+ if (need_comma)
+ data_ << ',';
+ else
+ need_comma = true;
+
+ AppendValue(value);
+ }
+ data_ << ']';
+}
+
+void JsonProtoConverter::AppendNumber(const NumberValue& number_value) {
+ if (number_value.has_float_value()) {
+ data_ << number_value.float_value().value();
+ } else if (number_value.has_exponent_value()) {
+ auto value = number_value.exponent_value();
+ data_ << value.base();
+ data_ << (value.use_uppercase() ? 'E' : 'e');
+ data_ << value.exponent();
+ } else if (number_value.has_exponent_frac_value()) {
+ auto value = number_value.exponent_value();
+ data_ << value.base();
+ data_ << (value.use_uppercase() ? 'E' : 'e');
+ data_ << value.exponent();
+ } else {
+ data_ << number_value.integer_value().value();
+ }
+}
+
+void JsonProtoConverter::AppendObject(const JsonObject& json_object) {
+ data_ << '{' << '"' << json_object.name() << '"' << ':';
+ AppendValue(json_object.value());
+ data_ << '}';
+}
+
+void JsonProtoConverter::AppendValue(const JsonValue& json_value) {
+ if (json_value.has_object_value()) {
+ AppendObject(json_value.object_value());
+ } else if (json_value.has_array_value()) {
+ AppendArray(json_value.array_value());
+ } else if (json_value.has_number_value()) {
+ AppendNumber(json_value.number_value());
+ } else if (json_value.has_string_value()) {
+ data_ << '"' << json_value.string_value().value() << '"';
+ } else if (json_value.has_boolean_value()) {
+ data_ << (json_value.boolean_value().value() ? "true" : "false");
+ } else {
+ data_ << "null";
+ }
+}
+
+std::string JsonProtoConverter::Convert(const JsonObject& json_object) {
+ AppendObject(json_object);
+ return data_.str();
+}
+
+std::string JsonProtoConverter::Convert(
+ const json_proto::ArrayValue& json_array) {
+ AppendArray(json_array);
+ return data_.str();
+}
+
+} // namespace json_proto
diff --git a/tests/fuzz/json_proto_converter.h b/tests/fuzz/json_proto_converter.h
new file mode 100644
index 0000000..ca52d67
--- /dev/null
+++ b/tests/fuzz/json_proto_converter.h
@@ -0,0 +1,43 @@
+// Copyright 2020 Google Inc.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+//
+////////////////////////////////////////////////////////////////////////////////
+
+#ifndef JSON_PROTO_CONVERTER_H_
+#define JSON_PROTO_CONVERTER_H_
+
+#include <sstream>
+#include <string>
+
+#include "LUKS2_plain_JSON.pb.h"
+
+namespace json_proto {
+
+class JsonProtoConverter {
+ public:
+ std::string Convert(const json_proto::JsonObject&);
+ std::string Convert(const json_proto::ArrayValue&);
+
+ private:
+ std::stringstream data_;
+
+ void AppendArray(const json_proto::ArrayValue&);
+ void AppendNumber(const json_proto::NumberValue&);
+ void AppendObject(const json_proto::JsonObject&);
+ void AppendValue(const json_proto::JsonValue&);
+};
+
+} // namespace json_proto
+
+#endif // TESTING_LIBFUZZER_PROTO_JSON_PROTO_CONVERTER_H_
diff --git a/tests/fuzz/oss-fuzz-build.sh b/tests/fuzz/oss-fuzz-build.sh
new file mode 100755
index 0000000..b2f643f
--- /dev/null
+++ b/tests/fuzz/oss-fuzz-build.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+
+function in_oss_fuzz()
+{
+ test -n "$FUZZING_ENGINE"
+}
+
+echo "Running cryptsetup OSS-Fuzz build script."
+env
+set -ex
+PWD=$(pwd)
+
+export LC_CTYPE=C.UTF-8
+
+export SRC=${SRC:-$PWD/build}
+export OUT="${OUT:-$PWD/out}"
+export DEPS_PATH=$SRC/static_lib_deps
+
+export PKG_CONFIG_PATH="$DEPS_PATH"/lib/pkgconfig
+
+export CC=${CC:-clang}
+export CXX=${CXX:-clang++}
+export LIB_FUZZING_ENGINE="${LIB_FUZZING_ENGINE:--fsanitize=fuzzer}"
+
+SANITIZER="${SANITIZER:-address -fsanitize-address-use-after-scope}"
+flags="-O1 -fno-omit-frame-pointer -gline-tables-only -DFUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION -fsanitize=$SANITIZER -fsanitize=fuzzer-no-link"
+
+export CFLAGS="${CFLAGS:-$flags} -I$DEPS_PATH/include"
+export CXXFLAGS="${CXXFLAGS:-$flags} -I$DEPS_PATH/include"
+export LDFLAGS="${LDFLAGS-} -L$DEPS_PATH/lib"
+
+ENABLED_FUZZERS=${ENABLED_FUZZERS:-crypt2_load_fuzz crypt2_load_ondisk_fuzz crypt2_load_proto_plain_json_fuzz}
+
+mkdir -p $SRC
+mkdir -p $OUT
+mkdir -p $DEPS_PATH
+cd $SRC
+
+LIBFUZZER_PATCH="$PWD/unpoison-mutated-buffers-from-libfuzzer.patch"
+in_oss_fuzz && LIBFUZZER_PATCH="$PWD/cryptsetup/tests/fuzz/unpoison-mutated-buffers-from-libfuzzer.patch"
+
+in_oss_fuzz && apt-get update && apt-get install -y \
+ make autoconf automake autopoint libtool pkg-config \
+ sharutils gettext expect keyutils ninja-build \
+ bison
+
+[ ! -d zlib ] && git clone --depth 1 https://github.com/madler/zlib.git
+[ ! -d xz ] && git clone https://git.tukaani.org/xz.git
+[ ! -d json-c ] && git clone --depth 1 https://github.com/json-c/json-c.git
+[ ! -d lvm2 ] && git clone --depth 1 https://sourceware.org/git/lvm2.git
+[ ! -d popt ] && git clone --depth 1 https://github.com/rpm-software-management/popt.git
+[ ! -d libprotobuf-mutator ] && git clone --depth 1 https://github.com/google/libprotobuf-mutator.git \
+ && [ "$SANITIZER" == "memory" ] && ( cd libprotobuf-mutator; patch -p1 < $LIBFUZZER_PATCH )
+[ ! -d openssl ] && git clone --depth 1 https://github.com/openssl/openssl
+[ ! -d util-linux ] && git clone --depth 1 https://github.com/util-linux/util-linux
+[ ! -d cryptsetup_fuzzing ] && git clone --depth 1 https://gitlab.com/cryptsetup/cryptsetup_fuzzing.git
+
+cd openssl
+./Configure --prefix="$DEPS_PATH" --libdir=lib no-shared no-module no-asm
+make build_generated
+make -j libcrypto.a
+make install_dev
+cd ..
+
+cd util-linux
+./autogen.sh
+./configure --prefix="$DEPS_PATH" --enable-static --disable-shared -disable-all-programs --enable-libuuid --enable-libblkid
+make -j
+make install
+cd ..
+
+cd zlib
+./configure --prefix="$DEPS_PATH" --static
+make -j
+make install
+cd ..
+
+cd xz
+./autogen.sh --no-po4a
+./configure --prefix="$DEPS_PATH" --enable-static --disable-shared
+make -j
+make install
+cd ..
+
+cd json-c
+mkdir -p build
+rm -fr build/*
+cd build
+cmake .. -DCMAKE_INSTALL_PREFIX="$DEPS_PATH" -DBUILD_SHARED_LIBS=OFF -DBUILD_STATIC_LIBS=ON
+make -j
+make install
+cd ../..
+
+cd lvm2
+./configure --prefix="$DEPS_PATH" --enable-static_link --disable-udev_sync --enable-pkgconfig --disable-selinux
+make -j libdm.device-mapper
+# build of dmsetup.static is broken
+# make install_device-mapper
+cp ./libdm/ioctl/libdevmapper.a "$DEPS_PATH"/lib/
+cp ./libdm/libdevmapper.h "$DEPS_PATH"/include/
+cp ./libdm/libdevmapper.pc "$PKG_CONFIG_PATH"
+cd ..
+
+cd popt
+# --no-undefined is incompatible with sanitizers
+sed -i -e 's/-Wl,--no-undefined //' src/CMakeLists.txt
+mkdir -p build
+rm -fr build/*
+cd build
+cmake .. -DCMAKE_INSTALL_PREFIX="$DEPS_PATH" -DBUILD_SHARED_LIBS=OFF
+make -j
+make install
+cd ../..
+
+cd libprotobuf-mutator
+mkdir -p build
+rm -fr build/*
+cd build
+cmake .. -GNinja \
+ -DCMAKE_INSTALL_PREFIX="$DEPS_PATH" \
+ -DPKG_CONFIG_PATH="$PKG_CONFIG_PATH" \
+ -DLIB_PROTO_MUTATOR_TESTING=OFF \
+ -DLIB_PROTO_MUTATOR_DOWNLOAD_PROTOBUF=ON
+ninja
+ninja install
+cd external.protobuf;
+cp -Rf bin lib include "$DEPS_PATH";
+cd ../../..
+
+if in_oss_fuzz; then
+ mkdir -p cryptsetup/tests/fuzz/build
+ ln -s ../../../../static_lib_deps cryptsetup/tests/fuzz/build/static_lib_deps
+ cd cryptsetup
+else
+ cd ../../..
+fi
+./autogen.sh
+./configure --enable-static --disable-asciidoc --disable-ssh-token --disable-udev --disable-selinux --with-crypto_backend=openssl --disable-shared --enable-fuzz-targets
+make clean
+make -j fuzz-targets
+
+for fuzzer in $ENABLED_FUZZERS; do
+ cp tests/fuzz/$fuzzer $OUT
+ cp $SRC/cryptsetup_fuzzing/${fuzzer}_seed_corpus.zip $OUT
+
+ # optionally copy the dictionary if it exists
+ if [ -e tests/fuzz/${fuzzer}.dict ]; then
+ cp tests/fuzz/${fuzzer}.dict $OUT
+ fi
+done
+
+cd $PWD
diff --git a/tests/fuzz/plain_json_proto_to_luks2.cc b/tests/fuzz/plain_json_proto_to_luks2.cc
new file mode 100644
index 0000000..8c56c15
--- /dev/null
+++ b/tests/fuzz/plain_json_proto_to_luks2.cc
@@ -0,0 +1,75 @@
+/*
+ * cryptsetup LUKS2 protobuf to image converter
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 <iostream>
+#include <string>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+
+#include "plain_json_proto_to_luks2_converter.h"
+
+using namespace json_proto;
+
+int main(int argc, char *argv[]) {
+ LUKS2_both_headers headers;
+ LUKS2ProtoConverter converter;
+ int fd;
+
+ std::string out_img_name;
+
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " <LUKS2 proto>\n";
+ return EXIT_FAILURE;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ std::cerr << "Failed to open " << argv[1] << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ google::protobuf::io::FileInputStream fileInput(fd);
+
+ if (!google::protobuf::TextFormat::Parse(&fileInput, &headers)) {
+ std::cerr << "Failed to parse protobuf " << argv[1] << std::endl;
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ close(fd);
+
+ out_img_name = argv[1];
+ out_img_name += ".img";
+
+ fd = open(out_img_name.c_str(), O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_TRUNC, 0644);
+ if (fd < 0) {
+ std::cerr << "Failed to open output file " << out_img_name << std::endl;
+ return EXIT_FAILURE;
+ }
+ converter.set_write_headers_only(false);
+ converter.convert(headers, fd);
+
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/fuzz/plain_json_proto_to_luks2_converter.cc b/tests/fuzz/plain_json_proto_to_luks2_converter.cc
new file mode 100644
index 0000000..823c0c5
--- /dev/null
+++ b/tests/fuzz/plain_json_proto_to_luks2_converter.cc
@@ -0,0 +1,153 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 "plain_json_proto_to_luks2_converter.h"
+#include "json_proto_converter.h"
+
+extern "C" {
+#include "src/cryptsetup.h"
+#include "luks2/luks2.h"
+#include <err.h>
+}
+
+namespace json_proto {
+
+void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid, const std::string &json_text) {
+ struct luks2_hdr_disk hdr = {};
+ int r;
+
+ if (hd)
+ crypt_hash_destroy(hd);
+ if (crypt_hash_init(&hd, "sha256"))
+ err(EXIT_FAILURE, "crypt_hash_init failed");
+
+
+ r = lseek(fd, offset, SEEK_SET);
+ if (r == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ switch (header_proto.magic()) {
+ case INVALID:
+ memset(&hdr.magic, 0, LUKS2_MAGIC_L);
+ break;
+ case FIRST:
+ memcpy(&hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
+ break;
+ case SECOND:
+ memcpy(&hdr.magic, LUKS2_MAGIC_2ND, LUKS2_MAGIC_L);
+ break;
+ }
+ hdr.version = cpu_to_be16(header_proto.version());
+ hdr.hdr_size = cpu_to_be64(header_proto.hdr_size());
+ hdr.seqid = cpu_to_be64(seqid);
+ strncpy(hdr.checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
+ hdr.checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
+ strncpy(hdr.uuid, "af7f64ea-3233-4581-946b-6187d812841e", LUKS2_UUID_L);
+ memset(hdr.salt, 1, LUKS2_SALT_L);
+
+
+ if (header_proto.has_selected_offset())
+ hdr.hdr_offset = cpu_to_be64(header_proto.selected_offset());
+ else
+ hdr.hdr_offset = cpu_to_be64(offset);
+
+ if (write_buffer(fd, &hdr, LUKS2_HDR_BIN_LEN) != LUKS2_HDR_BIN_LEN)
+ err(EXIT_FAILURE, "write_buffer failed");
+ if (crypt_hash_write(hd, (char*)&hdr, LUKS2_HDR_BIN_LEN))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+
+ size_t hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN;
+ uint8_t csum[LUKS2_CHECKSUM_L];
+
+ size_t write_size = json_text.length() > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text.length();
+ if (write_buffer(fd, json_text.c_str(), write_size) != (ssize_t)write_size)
+ err(EXIT_FAILURE, "write_buffer failed");
+ if (crypt_hash_write(hd, json_text.c_str(), write_size))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+
+ for (size_t i = 0; i < (hdr_json_area_len - write_size); i++) {
+ if (crypt_hash_write(hd, "\0", 1))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+ }
+
+ if (header_proto.use_correct_checksum()) {
+ if (lseek(fd, offset + offsetof(luks2_hdr_disk, csum), SEEK_SET) == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ int hash_size = crypt_hash_size("sha256");
+ if (hash_size <= 0)
+ err(EXIT_FAILURE, "crypt_hash_size failed");
+
+ if (crypt_hash_final(hd, (char*)csum, (size_t)hash_size))
+ err(EXIT_FAILURE, "crypt_hash_final failed");
+ if (write_buffer(fd, csum, hash_size) != hash_size)
+ err(EXIT_FAILURE, "write_buffer failed");
+ }
+}
+
+void LUKS2ProtoConverter::set_write_headers_only(bool headers_only) {
+ write_headers_only = headers_only;
+}
+
+void LUKS2ProtoConverter::convert(const LUKS2_both_headers &headers, int fd) {
+ uint64_t primary_seqid, secondary_seqid;
+ int result;
+
+ size_t out_size = headers.primary_header().hdr_size() + headers.secondary_header().hdr_size();
+
+ if (!write_headers_only)
+ out_size += KEYSLOTS_SIZE + DATA_SIZE;
+
+ result = ftruncate(fd, out_size);
+ if (result == -1)
+ err(EXIT_FAILURE, "truncate failed");
+
+ result = lseek(fd, 0, SEEK_SET);
+ if (result == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ switch (headers.seqid()) {
+ case EQUAL:
+ primary_seqid = 1;
+ secondary_seqid = 1;
+ break;
+ case PRIMARY_GREATER:
+ primary_seqid = 2;
+ secondary_seqid = 1;
+ break;
+ case SECONDARY_GREATER:
+ primary_seqid = 1;
+ secondary_seqid = 2;
+ break;
+ }
+
+ JsonProtoConverter converter;
+ std::string json_text = converter.Convert(headers.json_area());
+
+ emit_luks2_binary_header(headers.primary_header(), fd, 0, primary_seqid, json_text);
+ emit_luks2_binary_header(headers.secondary_header(), fd, headers.primary_header().hdr_size(), secondary_seqid, json_text);
+}
+
+LUKS2ProtoConverter::~LUKS2ProtoConverter() {
+ if (hd)
+ crypt_hash_destroy(hd);
+}
+} // namespace LUKS2_proto
diff --git a/tests/fuzz/plain_json_proto_to_luks2_converter.h b/tests/fuzz/plain_json_proto_to_luks2_converter.h
new file mode 100644
index 0000000..7decf9f
--- /dev/null
+++ b/tests/fuzz/plain_json_proto_to_luks2_converter.h
@@ -0,0 +1,58 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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.
+ */
+
+#ifndef LUKS2_PROTO_CONVERTER_H_
+#define LUKS2_PROTO_CONVERTER_H_
+
+#include <sstream>
+#include <string>
+#include <json-c/json.h>
+
+#include "LUKS2_plain_JSON.pb.h"
+extern "C" {
+#include "crypto_backend/crypto_backend.h"
+}
+
+namespace json_proto {
+
+class LUKS2ProtoConverter {
+ public:
+ ~LUKS2ProtoConverter();
+ void create_jobj(const LUKS2_both_headers &headers, uint64_t hdr_size);
+ void convert(const LUKS2_both_headers &headers, int fd);
+ void create_jobj(const LUKS2_both_headers &headers);
+ void emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid, const std::string &json_text);
+
+ void set_write_headers_only(bool headers_only);
+
+ const uint8_t *get_out_buffer();
+ size_t get_out_size();
+
+ static const uint64_t KEYSLOTS_SIZE = 3 * 1024 * 1024;
+ static const uint64_t DATA_SIZE = 16 * 1024 * 1024;
+ private:
+ bool write_headers_only = false;
+ struct crypt_hash *hd = NULL;
+};
+
+} // namespace LUKS2_proto
+
+#endif // LUKS2_PROTO_CONVERTER_H_
diff --git a/tests/fuzz/proto_to_luks2.cc b/tests/fuzz/proto_to_luks2.cc
new file mode 100644
index 0000000..4a27cad
--- /dev/null
+++ b/tests/fuzz/proto_to_luks2.cc
@@ -0,0 +1,75 @@
+/*
+ * cryptsetup LUKS2 protobuf to image converter
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 <iostream>
+#include <string>
+
+#include <fcntl.h>
+#include <unistd.h>
+
+#include <google/protobuf/text_format.h>
+#include <google/protobuf/io/zero_copy_stream_impl.h>
+
+#include "proto_to_luks2_converter.h"
+
+using namespace LUKS2_proto;
+
+int main(int argc, char *argv[]) {
+ LUKS2_both_headers headers;
+ LUKS2ProtoConverter converter;
+ int fd;
+
+ std::string out_img_name;
+
+ if (argc != 2) {
+ std::cerr << "Usage: " << argv[0] << " <LUKS2 proto>\n";
+ return EXIT_FAILURE;
+ }
+
+ fd = open(argv[1], O_RDONLY);
+ if (fd < 0) {
+ std::cerr << "Failed to open " << argv[1] << std::endl;
+ return EXIT_FAILURE;
+ }
+
+ google::protobuf::io::FileInputStream fileInput(fd);
+
+ if (!google::protobuf::TextFormat::Parse(&fileInput, &headers)) {
+ std::cerr << "Failed to parse protobuf " << argv[1] << std::endl;
+ close(fd);
+ return EXIT_FAILURE;
+ }
+ close(fd);
+
+ out_img_name = argv[1];
+ out_img_name += ".img";
+
+ fd = open(out_img_name.c_str(), O_RDWR|O_CREAT|O_EXCL|O_CLOEXEC|O_TRUNC, 0644);
+ if (fd < 0) {
+ std::cerr << "Failed to open output file " << out_img_name << std::endl;
+ return EXIT_FAILURE;
+ }
+ converter.set_write_headers_only(false);
+ converter.convert(headers, fd);
+
+ close(fd);
+ return EXIT_SUCCESS;
+}
diff --git a/tests/fuzz/proto_to_luks2_converter.cc b/tests/fuzz/proto_to_luks2_converter.cc
new file mode 100644
index 0000000..96a70b7
--- /dev/null
+++ b/tests/fuzz/proto_to_luks2_converter.cc
@@ -0,0 +1,604 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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 "proto_to_luks2_converter.h"
+#include <iostream>
+
+extern "C" {
+#include "src/cryptsetup.h"
+#include "luks2/luks2.h"
+#include <err.h>
+}
+
+namespace LUKS2_proto {
+
+std::string LUKS2ProtoConverter::string_uint64_to_string(const string_uint64 &str_u64) {
+ std::ostringstream os;
+
+ if (str_u64.negative())
+ os << "-";
+
+ if (str_u64.has_uint_num())
+ os << str_u64.uint_num();
+ else if (str_u64.has_string_num())
+ os << str_u64.string_num();
+
+ return os.str();
+}
+
+std::string LUKS2ProtoConverter::object_id_to_string(const object_id &oid) {
+ std::ostringstream os;
+
+ if (oid.has_int_id()) {
+ os << (oid.int_id() % 33) - 16;
+ } else if (oid.has_string_id()) {
+ os << oid.string_id();
+ }
+
+ return os.str();
+}
+
+std::string LUKS2ProtoConverter::hash_algorithm_to_string(const hash_algorithm type) {
+ switch (type) {
+ case HASH_ALG_SHA1:
+ return "sha1";
+ case HASH_ALG_SHA256:
+ return "sha256";
+ }
+}
+
+std::string LUKS2ProtoConverter::keyslot_area_type_to_string(const keyslot_area_type type) {
+ switch (type) {
+ case KEYSLOT_AREA_TYPE_RAW:
+ return "raw";
+ case KEYSLOT_AREA_TYPE_NONE:
+ return "none";
+ case KEYSLOT_AREA_TYPE_JOURNAL:
+ return "journal";
+ case KEYSLOT_AREA_TYPE_CHECKSUM:
+ return "checksum";
+ case KEYSLOT_AREA_TYPE_DATASHIFT:
+ return "datashift";
+ }
+}
+
+void LUKS2ProtoConverter::generate_keyslot_area(struct json_object *jobj_area, const keyslot_area_description &keyslot_area_desc) {
+ // mandatory fields
+ if (keyslot_area_desc.has_type())
+ json_object_object_add(jobj_area, "type", json_object_new_string(keyslot_area_type_to_string(keyslot_area_desc.type()).c_str()));
+ if (keyslot_area_desc.has_offset())
+ json_object_object_add(jobj_area, "offset", json_object_new_string(string_uint64_to_string(keyslot_area_desc.offset()).c_str()));
+ if (keyslot_area_desc.has_size())
+ json_object_object_add(jobj_area, "size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.size()).c_str()));
+
+ // raw type fields
+ if (keyslot_area_desc.has_encryption())
+ json_object_object_add(jobj_area, "encryption", json_object_new_string(keyslot_area_desc.encryption().c_str()));
+ if (keyslot_area_desc.has_key_size())
+ json_object_object_add(jobj_area, "key_size", json_object_new_int(keyslot_area_desc.key_size()));
+
+ // checksum type fields
+ if (keyslot_area_desc.has_hash())
+ json_object_object_add(jobj_area, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_area_desc.hash()).c_str()));
+ if (keyslot_area_desc.has_sector_size())
+ json_object_object_add(jobj_area, "sector_size", json_object_new_int(keyslot_area_desc.sector_size()));
+
+ // datashift type fields
+ if (keyslot_area_desc.has_shift_size())
+ json_object_object_add(jobj_area, "shift_size", json_object_new_string(string_uint64_to_string(keyslot_area_desc.shift_size()).c_str()));
+}
+
+std::string LUKS2ProtoConverter::keyslot_kdf_type_to_string(const keyslot_kdf_type type) {
+ switch (type) {
+ case KEYSLOT_KDF_TYPE_PBKDF2:
+ return "pbkdf2";
+ case KEYSLOT_KDF_TYPE_ARGON2I:
+ return "argon2i";
+ case KEYSLOT_KDF_TYPE_ARGON2ID:
+ return "argon2id";
+ }
+}
+
+void LUKS2ProtoConverter::generate_keyslot_kdf(struct json_object *jobj_kdf, const keyslot_kdf_description &keyslot_kdf_desc) {
+ // mandatory fields
+ if (keyslot_kdf_desc.has_type())
+ json_object_object_add(jobj_kdf, "type", json_object_new_string(keyslot_kdf_type_to_string(keyslot_kdf_desc.type()).c_str()));
+
+ if (keyslot_kdf_desc.has_salt())
+ json_object_object_add(jobj_kdf, "salt", json_object_new_string(keyslot_kdf_desc.salt().c_str()));
+ else
+ json_object_object_add(jobj_kdf, "salt", json_object_new_string("6vz4xK7cjan92rDA5JF8O6Jk2HouV0O8DMB6GlztVk="));
+
+ // pbkdf2 type
+ if (keyslot_kdf_desc.has_hash())
+ json_object_object_add(jobj_kdf, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_kdf_desc.hash()).c_str()));
+ if (keyslot_kdf_desc.has_iterations())
+ json_object_object_add(jobj_kdf, "iterations", json_object_new_int(keyslot_kdf_desc.iterations()));
+
+ // argon2i and argon2id types
+ if (keyslot_kdf_desc.has_time())
+ json_object_object_add(jobj_kdf, "time", json_object_new_int(keyslot_kdf_desc.time()));
+ if (keyslot_kdf_desc.has_memory())
+ json_object_object_add(jobj_kdf, "memory", json_object_new_int(keyslot_kdf_desc.memory()));
+ if (keyslot_kdf_desc.has_cpus())
+ json_object_object_add(jobj_kdf, "cpus", json_object_new_int(keyslot_kdf_desc.cpus()));
+}
+
+std::string LUKS2ProtoConverter::keyslot_af_type_to_string(const keyslot_af_type type) {
+ switch (type) {
+ case KEYSLOT_AF_TYPE_LUKS1:
+ return "luks1";
+ }
+}
+
+void LUKS2ProtoConverter::generate_keyslot_af(struct json_object *jobj_af, const keyslot_af_description &keyslot_af_desc) {
+ if (keyslot_af_desc.has_type())
+ json_object_object_add(jobj_af, "type", json_object_new_string(keyslot_af_type_to_string(keyslot_af_desc.type()).c_str()));
+ if (keyslot_af_desc.has_stripes())
+ json_object_object_add(jobj_af, "stripes", json_object_new_int(keyslot_af_desc.stripes()));
+ if (keyslot_af_desc.has_hash())
+ json_object_object_add(jobj_af, "hash", json_object_new_string(hash_algorithm_to_string(keyslot_af_desc.hash()).c_str()));
+}
+
+std::string LUKS2ProtoConverter::keyslot_type_to_string(const keyslot_type type) {
+ switch (type) {
+ case KEYSLOT_TYPE_LUKS2:
+ return "luks2";
+ case KEYSLOT_TYPE_REENCRYPT:
+ return "reencrypt";
+ case KEYSLOT_TYPE_PLACEHOLDER:
+ return "placeholder";
+ }
+}
+
+std::string LUKS2ProtoConverter::reencrypt_keyslot_mode_to_string(const reencrypt_keyslot_mode mode) {
+ switch (mode) {
+ case MODE_REENCRYPT:
+ return "reencrypt";
+ case MODE_ENCRYPT:
+ return "encrypt";
+ case MODE_DECRYPT:
+ return "decrypt";
+ }
+}
+
+std::string LUKS2ProtoConverter::reencrypt_keyslot_direction_to_string(const reencrypt_keyslot_direction direction) {
+ switch (direction) {
+ case DIRECTION_FORWARD:
+ return "forward";
+ case DIRECTION_BACKWARD:
+ return "backward";
+ }
+}
+
+void LUKS2ProtoConverter::generate_keyslot(struct json_object *jobj_keyslots, const keyslot_description &keyslot_desc) {
+ struct json_object *jobj_keyslot, *jobj_area, *jobj_kdf, *jobj_af;
+
+ jobj_keyslot = json_object_new_object();
+ if (keyslot_desc.has_type())
+ json_object_object_add(jobj_keyslot, "type", json_object_new_string(keyslot_type_to_string(keyslot_desc.type()).c_str()));
+ if (keyslot_desc.has_key_size())
+ json_object_object_add(jobj_keyslot, "key_size", json_object_new_int(keyslot_desc.key_size()));
+ if (keyslot_desc.has_priority())
+ json_object_object_add(jobj_keyslot, "priority", json_object_new_int(keyslot_desc.priority()));
+ if (keyslot_desc.has_mode())
+ json_object_object_add(jobj_keyslot, "mode", json_object_new_int(keyslot_desc.mode()));
+ if (keyslot_desc.has_direction())
+ json_object_object_add(jobj_keyslot, "direction", json_object_new_int(keyslot_desc.direction()));
+
+ /* Area object */
+ if (keyslot_desc.has_area()) {
+ jobj_area = json_object_new_object();
+ generate_keyslot_area(jobj_area, keyslot_desc.area());
+ json_object_object_add(jobj_keyslot, "area", jobj_area);
+ }
+
+ /* KDF object */
+ if (keyslot_desc.has_kdf()) {
+ jobj_kdf = json_object_new_object();
+ generate_keyslot_kdf(jobj_kdf, keyslot_desc.kdf());
+ json_object_object_add(jobj_keyslot, "kdf", jobj_kdf);
+ }
+
+ /* AF object */
+ if (keyslot_desc.has_af()) {
+ jobj_af = json_object_new_object();
+ generate_keyslot_af(jobj_af, keyslot_desc.af());
+ json_object_object_add(jobj_keyslot, "af", jobj_af);
+ }
+
+ json_object_object_add(jobj_keyslots, object_id_to_string(keyslot_desc.oid()).c_str(), jobj_keyslot);
+}
+
+void LUKS2ProtoConverter::generate_token(struct json_object *jobj_tokens, const token_description &token_desc) {
+ struct json_object *jobj_token, *jobj_keyslots;
+ jobj_token = json_object_new_object();
+
+ if (token_desc.has_type())
+ json_object_object_add(jobj_token, "type", json_object_new_string(token_desc.type().c_str()));
+
+ if (token_desc.has_key_description())
+ json_object_object_add(jobj_token, "key_description", json_object_new_string(token_desc.key_description().c_str()));
+
+ if (!token_desc.keyslots().empty()) {
+ jobj_keyslots = json_object_new_array();
+
+ for (const object_id& oid : token_desc.keyslots()) {
+ json_object_array_add(jobj_keyslots,
+ json_object_new_string(object_id_to_string(oid).c_str()));
+ }
+
+ /* Replace or add new keyslots array */
+ json_object_object_add(jobj_token, "keyslots", jobj_keyslots);
+ }
+
+ json_object_object_add(jobj_tokens, object_id_to_string(token_desc.oid()).c_str(), jobj_token);
+}
+
+void LUKS2ProtoConverter::generate_digest(struct json_object *jobj_digests, const digest_description &digest_desc) {
+ struct json_object *jobj_digest, *jobj_keyslots, *jobj_segments;
+
+ jobj_digest = json_object_new_object();
+
+ if (digest_desc.has_type())
+ json_object_object_add(jobj_digest, "type", json_object_new_string(keyslot_kdf_type_to_string(digest_desc.type()).c_str()));
+
+ if (!digest_desc.keyslots().empty()) {
+ jobj_keyslots = json_object_new_array();
+
+ for (const object_id& oid : digest_desc.keyslots()) {
+ json_object_array_add(jobj_keyslots,
+ json_object_new_string(object_id_to_string(oid).c_str()));
+ }
+
+ /* Replace or add new keyslots array */
+ json_object_object_add(jobj_digest, "keyslots", jobj_keyslots);
+ }
+
+ if (!digest_desc.segments().empty()) {
+ jobj_segments = json_object_new_array();
+
+ for (const object_id& oid : digest_desc.segments()) {
+ json_object_array_add(jobj_segments,
+ json_object_new_string(object_id_to_string(oid).c_str()));
+ }
+
+ /* Replace or add new segments array */
+ json_object_object_add(jobj_digest, "segments", jobj_segments);
+ }
+
+ if (digest_desc.has_salt())
+ json_object_object_add(jobj_digest, "salt", json_object_new_string(digest_desc.salt().c_str()));
+ if (digest_desc.has_digest())
+ json_object_object_add(jobj_digest, "digest", json_object_new_string(digest_desc.digest().c_str()));
+ if (digest_desc.has_hash())
+ json_object_object_add(jobj_digest, "hash", json_object_new_string(hash_algorithm_to_string(digest_desc.hash()).c_str()));
+ if (digest_desc.has_iterations())
+ json_object_object_add(jobj_digest, "iterations", json_object_new_int(digest_desc.iterations()));
+
+ json_object_object_add(jobj_digests, object_id_to_string(digest_desc.oid()).c_str(), jobj_digest);
+}
+
+std::string LUKS2ProtoConverter::segment_type_to_string(segment_type type) {
+ switch (type) {
+ case SEGMENT_TYPE_LINEAR:
+ return "linear";
+ case SEGMENT_TYPE_CRYPT:
+ return "crypt";
+ }
+}
+
+std::string LUKS2ProtoConverter::segment_flag_to_string(segment_flag flag) {
+ switch (flag) {
+ case IN_REENCRYPTION:
+ return "in-reencryption";
+ case BACKUP_FINAL:
+ return "backup-final";
+ case BACKUP_PREVIOUS:
+ return "backup-previous";
+ case BACKUP_MOVED_SEGMENT:
+ return "backup-moved-segment";
+ }
+}
+
+void LUKS2ProtoConverter::generate_segment_integrity(struct json_object *jobj_integrity, const segment_integrity_description &segment_integrity_desc) {
+ if (segment_integrity_desc.has_type())
+ json_object_object_add(jobj_integrity, "type", json_object_new_string(segment_integrity_desc.type().c_str()));
+ if (segment_integrity_desc.has_journal_encryption())
+ json_object_object_add(jobj_integrity, "journal_encryption", json_object_new_string(segment_integrity_desc.journal_encryption().c_str()));
+ if (segment_integrity_desc.has_journal_integrity())
+ json_object_object_add(jobj_integrity, "journal_integrity", json_object_new_string(segment_integrity_desc.journal_integrity().c_str()));
+}
+
+void LUKS2ProtoConverter::generate_segment(struct json_object *jobj_segments, const segment_description &segment_desc) {
+ json_object *jobj_flags, *jobj_integrity;
+ json_object *jobj_segment = json_object_new_object();
+
+ if (segment_desc.has_type())
+ json_object_object_add(jobj_segment, "type", json_object_new_string(segment_type_to_string(segment_desc.type()).c_str()));
+
+ if (segment_desc.has_offset())
+ json_object_object_add(jobj_segment, "offset", json_object_new_string(string_uint64_to_string(segment_desc.offset()).c_str()));
+ if (segment_desc.has_size())
+ json_object_object_add(jobj_segment, "size", json_object_new_string(string_uint64_to_string(segment_desc.size()).c_str()));
+
+ if (!segment_desc.flags().empty()) {
+ jobj_flags = json_object_new_array();
+
+ for (const int flag : segment_desc.flags()) {
+ json_object_array_add(jobj_flags,
+ json_object_new_string(segment_flag_to_string(segment_flag(flag)).c_str()));
+ }
+
+ /* Replace or add new flags array */
+ json_object_object_add(jobj_segment, "flags", jobj_flags);
+ }
+
+ if (segment_desc.has_iv_tweak())
+ json_object_object_add(jobj_segment, "iv_tweak", json_object_new_string(string_uint64_to_string(segment_desc.iv_tweak()).c_str()));
+ if (segment_desc.has_encryption())
+ json_object_object_add(jobj_segment, "encryption", json_object_new_string(segment_desc.encryption().c_str()));
+ if (segment_desc.has_sector_size())
+ json_object_object_add(jobj_segment, "sector_size", json_object_new_int(segment_desc.sector_size()));
+
+ if (segment_desc.has_integrity()) {
+ jobj_integrity = json_object_new_object();
+ generate_segment_integrity(jobj_integrity, segment_desc.integrity());
+ json_object_object_add(jobj_segment, "integrity", jobj_integrity);
+ }
+
+ json_object_object_add(jobj_segments, object_id_to_string(segment_desc.oid()).c_str(), jobj_segment);
+}
+
+void LUKS2ProtoConverter::create_jobj(const LUKS2_both_headers &headers) {
+ json_object *jobj_keyslots = NULL;
+ json_object *jobj_digests = NULL;
+ json_object *jobj_segments = NULL;
+ json_object *jobj_tokens = NULL;
+
+ const json_area_description &json_desc = headers.json_area();
+
+ jobj = json_object_new_object();
+ if (!jobj)
+ return;
+
+ jobj_keyslots = json_object_new_object();
+ for (const keyslot_description &keyslot_desc : json_desc.keyslots()) {
+ generate_keyslot(jobj_keyslots, keyslot_desc);
+ }
+ json_object_object_add(jobj, "keyslots", jobj_keyslots);
+
+ jobj_digests = json_object_new_object();
+ for (const digest_description &digest_desc : json_desc.digests()) {
+ generate_digest(jobj_digests, digest_desc);
+ }
+ json_object_object_add(jobj, "digests", jobj_digests);
+
+ jobj_segments = json_object_new_object();
+ for (const segment_description &segment_desc : json_desc.segments()) {
+ generate_segment(jobj_segments, segment_desc);
+ }
+ json_object_object_add(jobj, "segments", jobj_segments);
+
+ jobj_tokens = json_object_new_object();
+ for (const token_description &token_desc : json_desc.tokens()) {
+ generate_token(jobj_tokens, token_desc);
+ }
+ json_object_object_add(jobj, "tokens", jobj_tokens);
+
+ if (json_desc.has_config()) {
+ uint64_t hdr_size = json_desc.config().use_primary_hdr_size() ? headers.primary_header().hdr_size() : headers.secondary_header().hdr_size();
+ generate_config(json_desc.config(), hdr_size - LUKS2_HDR_BIN_LEN, KEYSLOTS_SIZE);
+ }
+}
+
+void LUKS2ProtoConverter::emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid) {
+ struct luks2_hdr_disk hdr = {};
+ int r;
+
+ if (hd)
+ crypt_hash_destroy(hd);
+ if (crypt_hash_init(&hd, "sha256"))
+ err(EXIT_FAILURE, "crypt_hash_init failed");
+
+
+ r = lseek(fd, offset, SEEK_SET);
+ if (r == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ switch (header_proto.magic()) {
+ case INVALID:
+ memset(&hdr.magic, 0, LUKS2_MAGIC_L);
+ break;
+ case FIRST:
+ memcpy(&hdr.magic, LUKS2_MAGIC_1ST, LUKS2_MAGIC_L);
+ break;
+ case SECOND:
+ memcpy(&hdr.magic, LUKS2_MAGIC_2ND, LUKS2_MAGIC_L);
+ break;
+ }
+ hdr.version = cpu_to_be16(header_proto.version());
+ hdr.hdr_size = cpu_to_be64(header_proto.hdr_size());
+ hdr.seqid = cpu_to_be64(seqid);
+ strncpy(hdr.checksum_alg, "sha256", LUKS2_CHECKSUM_ALG_L);
+ hdr.checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
+ strncpy(hdr.uuid, "af7f64ea-3233-4581-946b-6187d812841e", LUKS2_UUID_L);
+ memset(hdr.salt, 1, LUKS2_SALT_L);
+
+
+ if (header_proto.has_selected_offset())
+ hdr.hdr_offset = cpu_to_be64(header_proto.selected_offset());
+ else
+ hdr.hdr_offset = cpu_to_be64(offset);
+
+ if (write_buffer(fd, &hdr, LUKS2_HDR_BIN_LEN) != LUKS2_HDR_BIN_LEN)
+ err(EXIT_FAILURE, "write_buffer failed");
+ if (crypt_hash_write(hd, (char*)&hdr, LUKS2_HDR_BIN_LEN))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+
+ size_t hdr_json_area_len = header_proto.hdr_size() - LUKS2_HDR_BIN_LEN;
+ size_t json_text_len;
+ const char *json_text;
+ uint8_t csum[LUKS2_CHECKSUM_L];
+
+ if (jobj) {
+ json_text = json_object_to_json_string_ext((struct json_object *)jobj, JSON_C_TO_STRING_PLAIN | JSON_C_TO_STRING_NOSLASHESCAPE);
+ if (!json_text || !*json_text)
+ err(EXIT_FAILURE, "json_object_to_json_string_ext failed");
+
+ json_text_len = strlen(json_text);
+
+ size_t write_size = json_text_len > hdr_json_area_len - 1 ? hdr_json_area_len - 1 : json_text_len;
+ if (write_buffer(fd, json_text, write_size) != (ssize_t)write_size)
+ err(EXIT_FAILURE, "write_buffer failed");
+ if (crypt_hash_write(hd, json_text, write_size))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+
+ for (size_t i = 0; i < (hdr_json_area_len - write_size); i++) {
+ if (crypt_hash_write(hd, "\0", 1))
+ err(EXIT_FAILURE, "crypt_hash_write failed");
+ }
+ }
+
+ if (header_proto.use_correct_checksum()) {
+ if (lseek(fd, offset + offsetof(luks2_hdr_disk, csum), SEEK_SET) == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ int hash_size = crypt_hash_size("sha256");
+ if (hash_size <= 0)
+ err(EXIT_FAILURE, "crypt_hash_size failed");
+
+ if (crypt_hash_final(hd, (char*)csum, (size_t)hash_size))
+ err(EXIT_FAILURE, "crypt_hash_final failed");
+ if (write_buffer(fd, csum, hash_size) != hash_size)
+ err(EXIT_FAILURE, "write_buffer failed");
+ }
+}
+
+void LUKS2ProtoConverter::set_write_headers_only(bool headers_only) {
+ write_headers_only = headers_only;
+}
+
+void LUKS2ProtoConverter::convert(const LUKS2_both_headers &headers, int fd) {
+ uint64_t primary_seqid, secondary_seqid;
+ int result;
+
+ size_t out_size = headers.primary_header().hdr_size() + headers.secondary_header().hdr_size();
+
+ if (!write_headers_only)
+ out_size += KEYSLOTS_SIZE + DATA_SIZE;
+
+ result = ftruncate(fd, out_size);
+ if (result == -1)
+ err(EXIT_FAILURE, "truncate failed");
+
+ result = lseek(fd, 0, SEEK_SET);
+ if (result == -1)
+ err(EXIT_FAILURE, "lseek failed");
+
+ switch (headers.seqid()) {
+ case EQUAL:
+ primary_seqid = 1;
+ secondary_seqid = 1;
+ break;
+ case PRIMARY_GREATER:
+ primary_seqid = 2;
+ secondary_seqid = 1;
+ break;
+ case SECONDARY_GREATER:
+ primary_seqid = 1;
+ secondary_seqid = 2;
+ break;
+ }
+
+ create_jobj(headers);
+ emit_luks2_binary_header(headers.primary_header(), fd, 0, primary_seqid);
+ emit_luks2_binary_header(headers.secondary_header(), fd, headers.primary_header().hdr_size(), secondary_seqid);
+}
+
+std::string LUKS2ProtoConverter::config_flag_to_string(config_flag flag) {
+ switch (flag) {
+ case CONFIG_FLAG_ALLOW_DISCARDS:
+ return "allow-discards";
+ case CONFIG_FLAG_SAME_CPU_CRYPT:
+ return "same-cpu-crypt";
+ case CONFIG_FLAG_SUBMIT_FROM_CRYPT_CPUS:
+ return "submit-from-crypt-cpus";
+ case CONFIG_FLAG_NO_JOURNAL:
+ return "no-journal";
+ case CONFIG_FLAG_NO_READ_WORKQUEUE:
+ return "no-read-workqueue";
+ case CONFIG_FLAG_NO_WRITE_WORKQUEUE:
+ return "no-write-workqueue";
+ }
+}
+
+std::string LUKS2ProtoConverter::config_requirement_to_string(config_requirement requirement) {
+ switch (requirement) {
+ case CONFIG_REQUIREMENT_OFFLINE_REENCRYPT:
+ return "offline-reencrypt";
+ case CONFIG_REQUIREMENT_ONLINE_REENCRYPT_V2:
+ return "online-reencrypt-v2";
+ }
+}
+
+void LUKS2ProtoConverter::generate_config(const config_description &config_desc, uint64_t json_size, uint64_t keyslots_size) {
+ json_object *jobj_config, *jobj_flags, *jobj_requirements, *jobj_mandatory;
+ jobj_config = json_object_new_object();
+
+ json_object_object_add(jobj_config, "json_size", json_object_new_string(std::to_string(json_size).c_str()));
+ json_object_object_add(jobj_config, "keyslots_size", json_object_new_string(std::to_string(keyslots_size).c_str()));
+
+ if (!config_desc.config_flags().empty()) {
+ jobj_flags = json_object_new_array();
+
+ for (const int flag : config_desc.config_flags()) {
+ json_object_array_add(jobj_flags,
+ json_object_new_string(config_flag_to_string(config_flag(flag)).c_str()));
+ }
+
+ /* Replace or add new flags array */
+ json_object_object_add(jobj_config, "flags", jobj_flags);
+ }
+
+ if (!config_desc.requirements().empty()) {
+ jobj_requirements = json_object_new_object();
+ jobj_mandatory = json_object_new_array();
+
+ for (const int requirement : config_desc.requirements()) {
+ json_object_array_add(jobj_mandatory,
+ json_object_new_string(config_requirement_to_string(config_requirement(requirement)).c_str()));
+ }
+
+ /* Replace or add new requirements array */
+ json_object_object_add(jobj_requirements, "mandatory", jobj_mandatory);
+ json_object_object_add(jobj_config, "requirements", jobj_requirements);
+ }
+
+ json_object_object_add(jobj, "config", jobj_config);
+}
+
+LUKS2ProtoConverter::~LUKS2ProtoConverter() {
+ json_object_put(jobj);
+ if (hd)
+ crypt_hash_destroy(hd);
+}
+} // namespace LUKS2_proto
diff --git a/tests/fuzz/proto_to_luks2_converter.h b/tests/fuzz/proto_to_luks2_converter.h
new file mode 100644
index 0000000..9f926d0
--- /dev/null
+++ b/tests/fuzz/proto_to_luks2_converter.h
@@ -0,0 +1,91 @@
+/*
+ * cryptsetup LUKS2 custom mutator fuzz target
+ *
+ * Copyright (C) 2022-2023 Daniel Zatovic <daniel.zatovic@gmail.com>
+ * Copyright (C) 2022-2023 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.
+ */
+
+#ifndef LUKS2_PROTO_CONVERTER_H_
+#define LUKS2_PROTO_CONVERTER_H_
+
+#include <sstream>
+#include <string>
+#include <json-c/json.h>
+
+#include "LUKS2.pb.h"
+extern "C" {
+#include "crypto_backend/crypto_backend.h"
+}
+
+namespace LUKS2_proto {
+
+class LUKS2ProtoConverter {
+ public:
+ ~LUKS2ProtoConverter();
+ std::string string_uint64_to_string(const string_uint64 &str_u64);
+ std::string hash_algorithm_to_string(const hash_algorithm type);
+ std::string object_id_to_string(const object_id &oid);
+
+ std::string keyslot_area_type_to_string(const keyslot_area_type type);
+ std::string keyslot_kdf_type_to_string(const keyslot_kdf_type type);
+ std::string reencrypt_keyslot_mode_to_string(const reencrypt_keyslot_mode mode);
+ std::string keyslot_type_to_string(const keyslot_type type);
+ std::string reencrypt_keyslot_direction_to_string(const reencrypt_keyslot_direction direction);
+ std::string keyslot_af_type_to_string(const keyslot_af_type type);
+
+ std::string config_flag_to_string(config_flag flag);
+ std::string config_requirement_to_string(config_requirement requirements);
+
+ std::string segment_type_to_string(segment_type type);
+ std::string segment_flag_to_string(segment_flag flag);
+
+ void generate_keyslot(struct json_object *jobj_keyslots, const keyslot_description &keyslot_desc);
+ void generate_keyslot_area(struct json_object *jobj_area, const keyslot_area_description &keyslot_area_desc);
+ void generate_keyslot_kdf(struct json_object *jobj_kdf, const keyslot_kdf_description &keyslot_kdf_desc);
+ void generate_keyslot_af(struct json_object *jobj_af, const keyslot_af_description &keyslot_af_desc);
+
+ void generate_token(struct json_object *jobj_tokens, const token_description &token_desc);
+
+ void generate_digest(struct json_object *jobj_digests, const digest_description &digest_desc);
+
+ void generate_segment_integrity(struct json_object *jobj_integrity, const segment_integrity_description &segment_integrity_desc);
+ void generate_segment(struct json_object *jobj_segments, const segment_description &segment_desc);
+
+ void generate_config(const config_description &config_desc, uint64_t json_size, uint64_t keyslots_size);
+
+ void create_jobj(const LUKS2_both_headers &headers, uint64_t hdr_size);
+ void emit_luks2_binary_header(uint64_t offset, uint64_t seqid, bool is_primary, uint64_t hdr_size);
+ void convert(const LUKS2_both_headers &headers, int fd);
+ void create_jobj(const LUKS2_both_headers &headers);
+ void emit_luks2_binary_header(const LUKS2_header &header_proto, int fd, uint64_t offset, uint64_t seqid);
+
+ void set_write_headers_only(bool headers_only);
+
+ const uint8_t *get_out_buffer();
+ size_t get_out_size();
+
+ static const uint64_t KEYSLOTS_SIZE = 3 * 1024 * 1024;
+ static const uint64_t DATA_SIZE = 16 * 1024 * 1024;
+ private:
+ bool write_headers_only = false;
+ struct crypt_hash *hd = NULL;
+ struct ::json_object *jobj = NULL;
+};
+
+} // namespace LUKS2_proto
+
+#endif // LUKS2_PROTO_CONVERTER_H_
diff --git a/tests/fuzz/unpoison-mutated-buffers-from-libfuzzer.patch b/tests/fuzz/unpoison-mutated-buffers-from-libfuzzer.patch
new file mode 100644
index 0000000..1f48339
--- /dev/null
+++ b/tests/fuzz/unpoison-mutated-buffers-from-libfuzzer.patch
@@ -0,0 +1,29 @@
+diff --git a/src/libfuzzer/libfuzzer_mutator.cc b/src/libfuzzer/libfuzzer_mutator.cc
+index 34d144c..b671fd4 100644
+--- a/src/libfuzzer/libfuzzer_mutator.cc
++++ b/src/libfuzzer/libfuzzer_mutator.cc
+@@ -14,6 +14,8 @@
+
+ #include "src/libfuzzer/libfuzzer_mutator.h"
+
++#include <sanitizer/msan_interface.h>
++
+ #include <string.h>
+
+ #include <algorithm>
+@@ -64,6 +66,7 @@ template <class T>
+ T MutateValue(T v) {
+ size_t size =
+ LLVMFuzzerMutate(reinterpret_cast<uint8_t*>(&v), sizeof(v), sizeof(v));
++ __msan_unpoison(reinterpret_cast<uint8_t*>(&v), size);
+ memset(reinterpret_cast<uint8_t*>(&v) + size, 0, sizeof(v) - size);
+ return v;
+ }
+@@ -93,6 +96,7 @@ std::string Mutator::MutateString(const std::string& value,
+ result.resize(std::max(1, new_size));
+ result.resize(LLVMFuzzerMutate(reinterpret_cast<uint8_t*>(&result[0]),
+ value.size(), result.size()));
++ __msan_unpoison(reinterpret_cast<uint8_t*>(&result[0]), result.size());
+ return result;
+ }
+
diff --git a/tests/fvault2-compat-test b/tests/fvault2-compat-test
new file mode 100755
index 0000000..45022d2
--- /dev/null
+++ b/tests/fvault2-compat-test
@@ -0,0 +1,134 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+MAP=fvault2test
+TST_DIR=fvault2-images
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+[ -z "$srcdir" ] && srcdir="."
+
+function create_mapping()
+{
+ local image=$1
+ local passphrase=$2
+ echo -n "$passphrase" | "$CRYPTSETUP" open --type fvault2 --key-file - \
+ "$image" "$MAP"
+}
+
+function remove_mapping()
+{
+ [ -b "/dev/mapper/$MAP" ] && dmsetup remove --retry "$MAP"
+ rm -rf $TST_DIR
+}
+
+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."
+ remove_mapping
+ exit 77
+}
+
+function produce_dump()
+{
+ "$CRYPTSETUP" fvault2Dump "$1" || fail
+}
+
+function produce_dump_key()
+{
+ echo "$2" | "$CRYPTSETUP" fvault2Dump "$1" --dump-volume-key || fail
+}
+
+function check_dump()
+{
+ local dump=$1
+ local key=$2
+ local exp_value=$3
+ local regex="$key:\s*\(.*\)"
+ local value=$(echo "$dump" | sed -n "s|$regex|\1|p" | sed 's|\s*$||')
+ [ "$value" = "$exp_value" ] || fail \
+ "$key check failed: expected \"$exp_value\", got \"$value\""
+}
+
+function check_uuid()
+{
+ local exp_uuid=$1
+ local uuid=$(blkid -po value -s UUID "/dev/mapper/$MAP")
+ [ "$uuid" = "$exp_uuid" ] || fail \
+ "UUID check failed: expected \"$exp_uuid\", got \"$uuid\""
+}
+
+function check_sha256()
+{
+ local exp_sum=$1
+ local sum=$(sha256sum /dev/mapper/$MAP | head -c 64)
+ [ "$sum" = "$exp_sum" ] || fail \
+ "SHA256 sum check failed: expected \"$exp_sum\", got \"$sum\""
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+
+if [ ! -d $TST_DIR ]; then
+ tar xJSf $srcdir/fvault2-images.tar.xz --no-same-owner 2>/dev/null || skip "Incompatible tar."
+fi
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+echo "HEADER CHECK"
+IMG="$TST_DIR/small"
+PWD="heslo123"
+
+echo -n " $IMG"
+dump=$(produce_dump $IMG)
+check_dump "$dump" 'Physical volume UUID' fc52bfae-5a1f-4f9b-b3a6-f33303a0e401
+check_dump "$dump" 'Family UUID' 33a76caa-1481-4bc5-8d04-1ac1707c19c0
+check_dump "$dump" 'Logical volume offset' '67108864 [bytes]'
+check_dump "$dump" 'Logical volume size' '167772160 [bytes]'
+check_dump "$dump" 'PBKDF2 iterations' 204222
+check_dump "$dump" 'PBKDF2 salt' '2c 24 9e db 66 63 d6 fb cc 79 05 b7 a4 d7 27 52'
+dump=$(produce_dump_key $IMG heslo123)
+check_dump "$dump" 'Volume key' '20 73 4d 33 89 21 27 74 d7 61 0c 29 d7 32 88 09 16 f3 be 14 c4 b1 2a c7 aa f0 7e 5c cc 77 b3 19'
+echo $PWD | $CRYPTSETUP open --type fvault2 --test-passphrase $IMG || fail
+echo " [OK]"
+
+if [ $(id -u) != 0 ]; then
+ echo "WARNING: You must be root to run activation part of test, test skipped."
+ remove_mapping
+ exit 0
+fi
+
+echo "ACTIVATION CHECK"
+echo -n " $IMG"
+create_mapping $IMG heslo123
+check_uuid de124d8a-2164-394e-924f-8e28db0a09cb
+check_sha256 2c662e36c0f7e2f5583e6a939bbcbdc660805692d0fccaa45ad4052beb3b8e18
+echo " [OK]"
+
+remove_mapping
+exit 0
diff --git a/tests/fvault2-images.tar.xz b/tests/fvault2-images.tar.xz
new file mode 100644
index 0000000..99fab77
--- /dev/null
+++ b/tests/fvault2-images.tar.xz
Binary files differ
diff --git a/tests/generate-symbols-list b/tests/generate-symbols-list
new file mode 100755
index 0000000..33a2e23
--- /dev/null
+++ b/tests/generate-symbols-list
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 2
+}
+
+function generate() {
+ local ver=
+
+ while IFS= read -r line; do
+ local len=${#line}
+
+
+ if [ "${line:0:11}" = "CRYPTSETUP_" ]; then
+ local i=12
+ while [ $i -lt $len ]; do
+ if [ "${line:$i:1}" = "{" ]; then
+ ver=${line:0:$i}
+ break
+ fi
+ i=$((i+1))
+ done
+ elif [ "${line:0:6}" = "crypt_" -a -n "$ver" ]; then
+ printf 'CHECK_SYMBOL(%s, %s)\n' $line $ver
+ fi
+ done < <(tr -d '[:blank:];' < $1)
+}
+
+test $# -ge 1 || fail "usage: $0 <symbol_file>"
+
+test -f $1 || fail "$1 is not a file."
+
+generate $1
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..a7d3147
--- /dev/null
+++ b/tests/generators/generate-luks2-area-in-json-hdr-space-json0.img.sh
@@ -0,0 +1,44 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f0b74d7
--- /dev/null
+++ b/tests/generators/generate-luks2-argon2-leftover-params.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_obj_len=$(jq -c -M '.keyslots."1".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..5cba271
--- /dev/null
+++ b/tests/generators/generate-luks2-correct-full-json0.img.sh
@@ -0,0 +1,58 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ #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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..1365e0c
--- /dev/null
+++ b/tests/generators/generate-luks2-corrupted-hdr0-with-correct-chks.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr0
+}
+
+function check()
+{
+ lib_hdr0_checksum || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..fcbbb1e
--- /dev/null
+++ b/tests/generators/generate-luks2-corrupted-hdr1-with-correct-chks.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr1_checksum || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..925763e
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-both-hdrs.img.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in both binary headerer
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+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()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..ae8c595
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-hdr0.img.sh
@@ -0,0 +1,28 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in primary binary header
+#
+
+# 1 full target dir
+# 2 full source luks2 image
+
+function generate()
+{
+ CHKS0=$(echo "Arbitrary chosen string: D'oh!" | calc_sha256_checksum_stdin)
+ write_checksum $CHKS0 $TGT_IMG
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..a56695d
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-checksum-hdr1.img.sh
@@ -0,0 +1,29 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with bad checksum in secondary binary header
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ CHKS1=$(echo "Arbitrary chosen string: D'oh!" | calc_sha256_checksum_stdin)
+ write_checksum $CHKS1 $TMPDIR/hdr1
+ write_luks2_bin_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function check()
+{
+ lib_hdr1_checksum || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..13dea92
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c0.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..5cdc7ce
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c1.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..4122338
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-json-size-c2.img.sh
@@ -0,0 +1,55 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..8187b72
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c0.img.sh
@@ -0,0 +1,43 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..2ba1a9b
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c1.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f983438
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-keyslots-size-c2.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..616120b
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-object-type-json0.img.sh
@@ -0,0 +1,41 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ read -r json_str_res0 < $TMPDIR/json_res0
+ test "$json_str" = "$json_str_res0" || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..3f34692
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-opening-char-json0.img.sh
@@ -0,0 +1,41 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ IFS= read -r json_str_res0 < $TMPDIR/json_res0
+ test "$json_str" = "$json_str_res0" || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-invalid-tokens.img.sh b/tests/generators/generate-luks2-invalid-tokens.img.sh
new file mode 100755
index 0000000..9719cf7
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-tokens.img.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where keyslot is not of type object.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c 'del(.tokens) | .tokens = 42' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if .tokens != 42
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-invalid-top-objects.img.sh b/tests/generators/generate-luks2-invalid-top-objects.img.sh
new file mode 100755
index 0000000..174dc2c
--- /dev/null
+++ b/tests/generators/generate-luks2-invalid-top-objects.img.sh
@@ -0,0 +1,43 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where multiple top objects are not of type object.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c 'del(.tokens) | .tokens = 42 |
+ del(.digests) | .digests = 42 |
+ del(.keyslots) | .keyslots = [] |
+ del(.segments) | .segments = "hi"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.tokens != 42) or (.digests != 42) or (.keyslots != []) or (.segments != "hi")
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-keyslot-invalid-af.img.sh b/tests/generators/generate-luks2-keyslot-invalid-af.img.sh
new file mode 100755
index 0000000..99f7679
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-invalid-af.img.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where keyslot AF type is invalid.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c 'del(.keyslots."0".af.type) | .keyslots."0".af.type = 42' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."0".af.type != 42)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-keyslot-invalid-area-size.img.sh b/tests/generators/generate-luks2-keyslot-invalid-area-size.img.sh
new file mode 100755
index 0000000..723d58a
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-invalid-area-size.img.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where keyslot area object size is UINT64_MAX and will overflow with added length
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c '.keyslots."0"."area".size = "18446744073709551615"' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."0"."area".size != "18446744073709551615")
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-keyslot-invalid-area.img.sh b/tests/generators/generate-luks2-keyslot-invalid-area.img.sh
new file mode 100755
index 0000000..c41037e
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-invalid-area.img.sh
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where keyslot area object is not of type object.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c 'del(.keyslots."0".area) | .keyslots."0".area = 42' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."0".area != 42)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-keyslot-invalid-objects.img.sh b/tests/generators/generate-luks2-keyslot-invalid-objects.img.sh
new file mode 100755
index 0000000..5fcfef2
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-invalid-objects.img.sh
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate header with well-formed json format
+# where multiple keyslots objects are not of type object.
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+function generate()
+{
+ json_str=$(jq -c 'del(.keyslots."0".kdf) | .keyslots."0".kdf = 42 |
+ del(.keyslots."0".af) | .keyslots."0".af = 42' $TMPDIR/json0)
+ test ${#json_str} -lt $((LUKS2_JSON_SIZE*512)) || exit 2
+
+ write_luks2_json "$json_str" $TMPDIR/json0
+ write_luks2_json "$json_str" $TMPDIR/json1
+
+ lib_mangle_json_hdr0
+ lib_mangle_json_hdr1
+}
+
+function check()
+{
+ lib_hdr0_checksum || exit 2
+ lib_hdr1_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ jq -c 'if (.keyslots."0".kdf != 42) or (.keyslots."0".af != 42)
+ then error("Unexpected value in result json") else empty end' $TMPDIR/json_res0 || exit 5
+}
+
+lib_prepare $@
+generate
+check
+lib_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..49aeff1
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-missing-digest.img.sh
@@ -0,0 +1,43 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json_res0)
+ test $((arr_len-1)) -eq $new_arr_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..5ba55f1
--- /dev/null
+++ b/tests/generators/generate-luks2-keyslot-too-many-digests.img.sh
@@ -0,0 +1,41 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_arr_len=$(jq -c -M '.digests."1".keyslots | length' $TMPDIR/json_res0)
+ test 1 -eq $new_arr_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..2a44678
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-128k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..79cccbd
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-128k.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f0e6e8d
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-16k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..25c19c1
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-1m-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..9228fe5
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-1m.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..b4c1027
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-256k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..60ec878
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-256k.img.sh
@@ -0,0 +1,62 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..0c68905
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-2m-secondary.img.sh
@@ -0,0 +1,63 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..0dbb521
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-2m.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..effd244
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-32k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f970144
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-32k.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f423850
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-4m-secondary.img.sh
@@ -0,0 +1,63 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..b15ad4b
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-4m.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..4980816
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-512k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f3da37f
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-512k.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..3913f03
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c0.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..b01f933
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-area-c1.img.sh
@@ -0,0 +1,63 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..5b8517a
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-inv-keyslots-size-c0.img.sh
@@ -0,0 +1,63 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..9635ab7
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k-secondary.img.sh
@@ -0,0 +1,64 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $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
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..50941b8
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-64k.img.sh
@@ -0,0 +1,61 @@
+#!/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 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh b/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh
new file mode 100755
index 0000000..d2ddd61
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-invalid-secondary.img.sh
@@ -0,0 +1,63 @@
+#!/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 generate()
+{
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_MDA_SIZE_BOGUS_BYTES=$((TEST_MDA_SIZE*512*2*1024))
+ 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BOGUS_BYTES
+
+ write_bin_hdr_offset $TMPDIR/hdr1 $TEST_MDA_SIZE_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE
+}
+
+function check()
+{
+ lib_hdr0_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-metadata-size-invalid.img.sh b/tests/generators/generate-luks2-metadata-size-invalid.img.sh
new file mode 100755
index 0000000..745fc5c
--- /dev/null
+++ b/tests/generators/generate-luks2-metadata-size-invalid.img.sh
@@ -0,0 +1,61 @@
+#!/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 generate()
+{
+ TEST_MDA_SIZE=$LUKS2_HDR_SIZE_1M
+
+ TEST_MDA_SIZE_BYTES=$((TEST_MDA_SIZE*512))
+ TEST_MDA_SIZE_BOGUS_BYTES=$((TEST_MDA_SIZE*512*2*1024))
+ 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_luks2_json "$json_str" $TMPDIR/json1 $TEST_JSN_SIZE
+
+ write_bin_hdr_size $TMPDIR/hdr0 $TEST_MDA_SIZE_BOGUS_BYTES
+ write_bin_hdr_size $TMPDIR/hdr1 $TEST_MDA_SIZE_BOGUS_BYTES
+
+ lib_mangle_json_hdr0 $TEST_MDA_SIZE $TEST_JSN_SIZE
+ lib_mangle_json_hdr1 $TEST_MDA_SIZE $TEST_JSN_SIZE kill
+}
+
+function check()
+{
+ lib_hdr1_killed $TEST_MDA_SIZE || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..a0ca53c
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-keyslot-referenced-in-digest.img.sh
@@ -0,0 +1,45 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_arr_len=$(jq -c -M '.digests."0".keyslots | length' $TMPDIR/json_res0)
+ test $((arr_len+1)) -eq $new_arr_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..84d7ed2
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-keyslot-referenced-in-token.img.sh
@@ -0,0 +1,43 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_arr_len=$(jq -c -M '.tokens."0".keyslots | length' $TMPDIR/json_res0)
+ test $new_arr_len -eq 2 || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..300c2dc
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-segment-referenced-in-digest.img.sh
@@ -0,0 +1,45 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_arr_len=$(jq -c -M '.digests."0".segments | length' $TMPDIR/json_res0)
+ test $((arr_len+1)) -eq $new_arr_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..9c5ed0b
--- /dev/null
+++ b/tests/generators/generate-luks2-missing-trailing-null-byte-json0.img.sh
@@ -0,0 +1,60 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..6f4aa7d
--- /dev/null
+++ b/tests/generators/generate-luks2-non-null-byte-beyond-json0.img.sh
@@ -0,0 +1,43 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..18abf23
--- /dev/null
+++ b/tests/generators/generate-luks2-non-null-bytes-beyond-json0.img.sh
@@ -0,0 +1,47 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+
+ _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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..23883bb
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c0-json0.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..0733627
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c1-json0.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..6699b38
--- /dev/null
+++ b/tests/generators/generate-luks2-overlapping-areas-c2-json0.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..e035f94
--- /dev/null
+++ b/tests/generators/generate-luks2-pbkdf2-leftover-params-0.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_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..d82c2bd
--- /dev/null
+++ b/tests/generators/generate-luks2-pbkdf2-leftover-params-1.img.sh
@@ -0,0 +1,42 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || exit 2
+ lib_hdr0_checksum || exit 2
+
+ read_luks2_json0 $TGT_IMG $TMPDIR/json_res0
+ new_obj_len=$(jq -c -M '.keyslots."2".kdf | length' $TMPDIR/json_res0)
+ test $((obj_len+2)) -eq $new_obj_len || exit 2
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh b/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh
new file mode 100755
index 0000000..ca17aac
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-empty-encryption.img.sh
@@ -0,0 +1,39 @@
+#!/bin/bash
+
+. lib.sh
+
+#
+# *** Description ***
+#
+# generate primary header with segment empty encryption field
+#
+# secondary header is corrupted on purpose as well
+#
+
+# $1 full target dir
+# $2 full source luks2 image
+
+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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..e92bc2a
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-encryption.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..77beb53
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-ivoffset.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..0609533
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-missing-sectorsize.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..9d7e584
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-encryption.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..0830a16
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-ivoffset.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..069b6c0
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-0.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..c310ff1
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-1.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..b4b8b39
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-crypt-wrong-sectorsize-2.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..6d5811e
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-offset.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..579858f
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-size.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..5b74c5d
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-missing-type.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..798c5be
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-two.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..814344a
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-unknown-type.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..3ba9d47
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-backup-key-0.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..11a94d7
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-backup-key-1.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..72da1f1
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-flags-element.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..19d6340
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-flags.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..c9b1b50
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-offset.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..b9227a7
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-0.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..6be5031
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-1.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..311c0e8
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-size-2.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..c041157
--- /dev/null
+++ b/tests/generators/generate-luks2-segment-wrong-type.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..f966e1d
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-max-segment-size.img.sh
@@ -0,0 +1,40 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..4e064e4
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-overflow-segment-size.img.sh
@@ -0,0 +1,38 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_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..6687f35
--- /dev/null
+++ b/tests/generators/generate-luks2-uint64-signed-segment-size.img.sh
@@ -0,0 +1,39 @@
+#!/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 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
+
+ lib_mangle_json_hdr0_kill_hdr1
+}
+
+function check()
+{
+ lib_hdr1_killed || 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
+}
+
+lib_prepare $@
+generate
+check
+lib_cleanup
diff --git a/tests/generators/lib.sh b/tests/generators/lib.sh
new file mode 100644
index 0000000..c0e9cc1
--- /dev/null
+++ b/tests/generators/lib.sh
@@ -0,0 +1,283 @@
+#!/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
+
+# to be set by individual generator
+TGT_IMG=""
+SRC_IMG=""
+
+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
+}
+
+# generic header helpers
+# $TMPDIR/json0 - JSON hdr1
+# $TMPDIR/json1 - JSON hdr2
+# $TMPDIR/hdr0 - bin hdr1
+# $TMPDIR/hdr1 - bin hdr2
+
+# 1:target_dir 2:source_image
+function lib_prepare()
+{
+ test $# -eq 2 || exit 1
+
+ TGT_IMG=$1/$(test_img_name $0)
+ SRC_IMG=$2
+
+ # wipe checksums
+ CHKS0=0
+ CHKS1=0
+
+ cp $SRC_IMG $TGT_IMG
+ test -d $TMPDIR || mkdir $TMPDIR
+ read_luks2_json0 $TGT_IMG $TMPDIR/json0
+ read_luks2_json1 $TGT_IMG $TMPDIR/json1
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr0
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr1
+}
+
+function lib_cleanup()
+{
+ rm -f $TMPDIR/*
+ rm -fd $TMPDIR
+}
+
+function lib_mangle_json_hdr0()
+{
+ local mda_sz=${1:-}
+ local jsn_sz=${2:-}
+ local kill_hdr=${3:-}
+
+ merge_bin_hdr_with_json $TMPDIR/hdr0 $TMPDIR/json0 $TMPDIR/area0 $jsn_sz
+ erase_checksum $TMPDIR/area0
+ CHKS0=$(calc_sha256_checksum_file $TMPDIR/area0)
+ write_checksum $CHKS0 $TMPDIR/area0
+ test -n "$kill_hdr" && kill_bin_hdr $TMPDIR/area0
+ write_luks2_hdr0 $TMPDIR/area0 $TGT_IMG $mda_sz
+}
+
+function lib_mangle_json_hdr1()
+{
+ local mda_sz=${1:-}
+ local jsn_sz=${2:-}
+ local kill_hdr=${3:-}
+
+ merge_bin_hdr_with_json $TMPDIR/hdr1 $TMPDIR/json1 $TMPDIR/area1 $jsn_sz
+ erase_checksum $TMPDIR/area1
+ CHKS1=$(calc_sha256_checksum_file $TMPDIR/area1)
+ write_checksum $CHKS1 $TMPDIR/area1
+ test -n "$kill_hdr" && kill_bin_hdr $TMPDIR/area1
+ write_luks2_hdr1 $TMPDIR/area1 $TGT_IMG $mda_sz
+}
+
+function lib_mangle_json_hdr0_kill_hdr1()
+{
+ lib_mangle_json_hdr0
+
+ kill_bin_hdr $TMPDIR/hdr1
+ write_luks2_hdr1 $TMPDIR/hdr1 $TGT_IMG
+}
+
+function lib_hdr0_killed()
+{
+ local mda_sz=${1:-}
+
+ read_luks2_bin_hdr0 $TGT_IMG $TMPDIR/hdr_res0 $mda_sz
+ local str_res0=$(head -c 6 $TMPDIR/hdr_res0)
+ test "$str_res0" = "VACUUM"
+}
+
+function lib_hdr1_killed()
+{
+ local mda_sz=${1:-}
+
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1 $mda_sz
+ local str_res1=$(head -c 6 $TMPDIR/hdr_res1)
+ test "$str_res1" = "VACUUM"
+}
+
+function lib_hdr0_checksum()
+{
+ local chks_res0=$(read_sha256_checksum $TGT_IMG)
+ test "$CHKS0" = "$chks_res0"
+}
+
+function lib_hdr1_checksum()
+{
+ read_luks2_bin_hdr1 $TGT_IMG $TMPDIR/hdr_res1
+ local chks_res1=$(read_sha256_checksum $TMPDIR/hdr_res1)
+ test "$CHKS1" = "$chks_res1"
+}
diff --git a/tests/img_fs_ext4.img.xz b/tests/img_fs_ext4.img.xz
new file mode 100644
index 0000000..de688f0
--- /dev/null
+++ b/tests/img_fs_ext4.img.xz
Binary files differ
diff --git a/tests/img_fs_vfat.img.xz b/tests/img_fs_vfat.img.xz
new file mode 100644
index 0000000..5ecfa67
--- /dev/null
+++ b/tests/img_fs_vfat.img.xz
Binary files differ
diff --git a/tests/img_fs_xfs.img.xz b/tests/img_fs_xfs.img.xz
new file mode 100644
index 0000000..a7034a7
--- /dev/null
+++ b/tests/img_fs_xfs.img.xz
Binary files differ
diff --git a/tests/integrity-compat-test b/tests/integrity-compat-test
new file mode 100755
index 0000000..208eafb
--- /dev/null
+++ b/tests/integrity-compat-test
@@ -0,0 +1,651 @@
+#!/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_NAME2=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_NAME2 ] && dmremove $DEV_NAME2
+ [ -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 5 ] && {
+ DM_INTEGRITY_RESIZE_SUPPORTED=1
+ }
+ [ $VER_MIN -gt 6 ] && {
+ DM_INTEGRITY_HMAC_FIX=1
+ }
+ [ $VER_MIN -gt 7 ] && {
+ DM_INTEGRITY_RESET=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()
+{
+ command -v valgrind >/dev/null || 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
+ if [[ $1 =~ "sha2" || $1 =~ "crc" ]] ; then
+ fail "Cannot format device."
+ fi
+ echo "[N/A]"
+ return
+ 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 2>&1
+ if [ $? -ne 0 ] ; then
+ if [[ $2 =~ "sha2" || $2 =~ "crc" ]] ; then
+ fail "Cannot format device."
+ fi
+ echo "[N/A]"
+ return
+ fi
+ 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]"
+}
+
+check_device_size() # device_name expected_size error_message
+{
+ CURRENT_SIZE=$(dmsetup table | grep $1 | cut -d' ' -f 3)
+ [ $CURRENT_SIZE -eq $2 ] || fail "$3: expected $1 to be of size $2, but is $CURRENT_SIZE"
+}
+
+test_resize() # description detached_metadata wipe args
+{
+ echo -n "$1"
+ if [ -z "$DM_INTEGRITY_RESIZE_SUPPORTED" ] ; then
+ echo "[N/A]"
+ return
+ fi
+
+ args="$4"
+ if [ $2 -ne 0 ] ; then
+ echo -n "[DETACHED]"
+ else
+ echo -n "[INTERLEAVE]"
+ fi
+ if [ $3 -ne 0 ] ; then
+ wipe_flag="--wipe"
+ echo -n "[WIPE]"
+ else
+ wipe_flag=""
+ echo -n "[RECALCULATE]"
+ fi
+
+ add_device
+ if [ $2 -ne 0 ] ; then
+ echo -n "[FORMAT]"
+ $INTSETUP format -q $args $DEV2 --data-device $DEV >/dev/null 2>&1 || fail "Cannot format device."
+ echo -n "[ACTIVATE]"
+ $INTSETUP open -q $args $DEV2 $DEV_NAME --data-device $DEV >/dev/null 2>&1 || fail "Cannot activate device."
+ else
+ echo -n "[FORMAT]"
+ $INTSETUP format -q $args $DEV >/dev/null 2>&1 || fail "Cannot format device."
+ echo -n "[ACTIVATE]"
+ $INTSETUP open -q $args $DEV $DEV_NAME >/dev/null 2>&1 || fail "Cannot activate device."
+ fi
+
+ if [ $2 -ne 0 ] ; then
+ # the whole device has 32MiB, if metadata is detached
+ WHOLE_DISK_SIZE=65536
+ else
+ WHOLE_DISK_SIZE=$(dmsetup table | grep $DEV_NAME | cut -d' ' -f 3)
+ fi
+
+ echo -n "[SHRINK]"
+ $INTSETUP resize -q $wipe_flag $DEV_NAME --device-size 1MiB || fail "Failed to resize the device to 1MiB."
+ dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Errors detected after shrink."
+ check_device_size $DEV_NAME $(( 1024*1024 / 512 )) "Shrinking device failed"
+
+ echo -n "[FILL]"
+ $INTSETUP resize -q $wipe_flag $DEV_NAME --device-size 0 || fail "Failed to resize the device to maximum size."
+ dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Errors detected after resize to maximum size."
+ check_device_size $DEV_NAME $WHOLE_DISK_SIZE "Resizing disk to maximum size failed"
+
+ echo -n "[EXPAND FIXED]"
+ fallocate $DEV --len 64M
+ $INTSETUP resize -q $wipe_flag $DEV_NAME --device-size 40MiB || fail "Failed to expand the device to a fixed size."
+ dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Errors detected after expanding to a fixed size."
+ check_device_size $DEV_NAME $(( 40*1024*1024 / 512 )) "Resizing disk after expanding to a fixed size failed"
+
+ echo -n "[FILL]"
+ $INTSETUP resize -q $wipe_flag $DEV_NAME --device-size 0 >/dev/null 2>&1 || fail "Failed to resize the device to maximum size after increasing image size."
+ dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Error detection failed after increasing image size."
+ CURRENT_SIZE=$(dmsetup table | grep $DEV_NAME | cut -d' ' -f 3)
+ [ $CURRENT_SIZE -ge $(( 40*1024*1024 / 512 )) ] || fail "Growing integrity device failed $CURRENT_SIZE is not greater than 40MB ($(( 40*1024*1024 / 512 )) blocks)."
+ if [ $2 -ne 0 ] ; then
+ [ $CURRENT_SIZE -eq 131072 ] || fail "Growing integrity device failed $CURRENT_SIZE is not equal to 64MB (131072 blocks)."
+ fi
+
+ echo -n "[REMOVE]"
+ $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."
+command -v blockdev >/dev/null || skip "Cannot find blockdev utility, test skipped."
+
+[ -n "$VALG" ] && valgrind_setup && INTSETUP=valgrind_run
+command -v hexdump >/dev/null || skip "WARNING: hexdump tool required."
+command -v xxd >/dev/null || skip "WARNING: xxd 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 xxhash64 xxhash64 0 8 512 6ff6bb889a8485f1fb26aa82671ff5da64f60381fc469e31d7be6094241eee09
+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 xxhash64 0 8 512
+int_error_detection J xxhash64 0 8 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
+
+command -v xxd >/dev/null || 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 -n "[OK]"
+ if [ -n "$DM_INTEGRITY_RESET" ] ; then
+ $INTSETUP open $DEV $DEV_NAME -I sha256 --integrity-recalculate-reset || fail "Cannot activate device."
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=1M 2>/dev/null || fail "Cannot reset recalculate tags in-kernel"
+ int_check_sum_only 08f63eb27fb9ce2ce903b0a56429c68ce5e209253ba42154841ef045a53839d7
+ $INTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ echo "[RESET OK]"
+ else
+ echo "[RESET N/A]"
+ fi
+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_NAME2 <<EOF
+0 16284 linear $DEV_LOOP 0
+16284 80000000000 zero
+EOF
+ [ ! -b /dev/mapper/$DEV_NAME2 ] && fail
+ $INTSETUP format -q -s 512 --no-wipe /dev/mapper/$DEV_NAME2
+ $INTSETUP open /dev/mapper/$DEV_NAME2 $DEV_NAME || fail
+ D_SIZE=$($INTSETUP dump /dev/mapper/$DEV_NAME2 | 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 "Deferred removal of device:"
+add_device
+$INTSETUP format -q $DEV || fail "Cannot format device."
+$INTSETUP open $DEV $DEV_NAME || fail "Cannot activate device."
+dmsetup create $DEV_NAME2 --table "0 8 linear /dev/mapper/$DEV_NAME 0"
+[ ! -b /dev/mapper/$DEV_NAME2 ] && fail
+$INTSETUP close $DEV_NAME >/dev/null 2>&1 && fail
+$INTSETUP -q status $DEV_NAME >/dev/null 2>&1 || fail
+$INTSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" || fail
+ $INTSETUP close --cancel-deferred $DEV_NAME >/dev/null 2>&1
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" >/dev/null 2>&1 && fail
+ $INTSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+ dmsetup remove $DEV_NAME2 || fail
+ $INTSETUP -q status $DEV_NAME >/dev/null 2>&1 && fail
+ echo "[OK]"
+else
+ dmsetup remove $DEV_NAME2 >/dev/null 2>&1
+ $INTSETUP close $DEV_NAME >/dev/null 2>&1
+ 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
+
+# shrinking the mapping should also work on older kernels
+echo -n "[INTEGRITY BASIC RESIZE NOKEY]"
+add_device
+ARGS="--integrity crc32"
+
+echo -n "[FORMAT]"
+$INTSETUP format -q $DEV $ARGS || fail "Cannot format device."
+echo -n "[ACTIVATE]"
+$INTSETUP open -q $DEV $DEV_NAME $ARGS >/dev/null 2>&1 || fail "Cannot activate device."
+echo -n "[SHRINK]"
+$INTSETUP resize $DEV_NAME --device-size 1MiB >/dev/null 2>&1 || fail "Failed to resize the device to 1MiB."
+check_device_size $DEV_NAME $(( 1024*1024 / 512 )) "Shrinking device failed"
+dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Errors detectied after resize."
+echo "[OK]"
+
+echo -n "[INTEGRITY BASIC RESIZE KEY]"
+add_device
+
+ARGS="--integrity hmac-sha256 --integrity-key-size 128 --integrity-key-file $KEY_FILE --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE --journal-integrity-key-size 128 --journal-crypt ctr-aes --journal-crypt-key-size 16 --journal-crypt-key-file $KEY_FILE"
+
+echo -n "[FORMAT]"
+$INTSETUP format -q $DEV $ARGS || fail "Cannot format device."
+echo -n "[ACTIVATE]"
+$INTSETUP open -q $DEV $DEV_NAME $ARGS >/dev/null 2>&1 || fail "Cannot activate device."
+echo -n "[SHRINK]"
+$INTSETUP resize $DEV_NAME --device-size 1MiB >/dev/null 2>&1 || fail "Failed to resize the device to 1MiB."
+check_device_size $DEV_NAME $(( 1024*1024 / 512 )) "Shrinking device failed"
+dd if=/dev/mapper/$DEV_NAME >/dev/null 2>&1 || fail "Errors detectied after resize."
+echo "[OK]"
+
+test_resize "[INTEGRITY RESIZE NOKEY]" 0 0 "--integrity crc32"
+test_resize "[INTEGRITY RESIZE NOKEY]" 0 1 "--integrity crc32"
+test_resize "[INTEGRITY RESIZE NOKEY DETACHED]" 1 0 "--integrity crc32"
+test_resize "[INTEGRITY RESIZE NOKEY DETACHED]" 1 1 "--integrity crc32"
+if [ -n "$DM_INTEGRITY_HMAC_FIX" ] ; then
+ test_resize "[INTEGRITY RESIZE KEY]" 0 0 "--integrity hmac-sha256 --integrity-key-size 128 --integrity-key-file $KEY_FILE --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE --journal-integrity-key-size 128 --journal-crypt ctr-aes --journal-crypt-key-size 16 --journal-crypt-key-file $KEY_FILE"
+ test_resize "[INTEGRITY RESIZE KEY]" 0 1 "--integrity hmac-sha256 --integrity-key-size 128 --integrity-key-file $KEY_FILE --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE --journal-integrity-key-size 128 --journal-crypt ctr-aes --journal-crypt-key-size 16 --journal-crypt-key-file $KEY_FILE"
+ test_resize "[INTEGRITY RESIZE KEY DETACHED]" 1 0 "--integrity hmac-sha256 --integrity-key-size 128 --integrity-key-file $KEY_FILE --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE --journal-integrity-key-size 128 --journal-crypt ctr-aes --journal-crypt-key-size 16 --journal-crypt-key-file $KEY_FILE"
+ test_resize "[INTEGRITY RESIZE KEY DETACHED]" 1 1 "--integrity hmac-sha256 --integrity-key-size 128 --integrity-key-file $KEY_FILE --journal-integrity hmac-sha256 --journal-integrity-key-file $KEY_FILE --journal-integrity-key-size 128 --journal-crypt ctr-aes --journal-crypt-key-size 16 --journal-crypt-key-file $KEY_FILE"
+fi
+
+cleanup
diff --git a/tests/keyring-compat-test b/tests/keyring-compat-test
new file mode 100755
index 0000000..ea88c21
--- /dev/null
+++ b/tests/keyring-compat-test
@@ -0,0 +1,234 @@
+#!/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="aaablabl"
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+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 >/dev/null 2>&1
+
+ rm -f $CHKS_DMCRYPT $CHKS_KEYRING
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 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() {
+ rmmod scsi_debug >/dev/null 2>&1
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 2
+ DEV=$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+
+ DEV="/dev/$DEV"
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+command -v dmsetup >/dev/null || skip "Cannot find dmsetup, test skipped"
+command -v keyctl >/dev/null || skip "Cannot find keyctl, test skipped"
+command -v xxd >/dev/null || skip "Cannot find xxd, test skipped"
+command -v sha256sum >/dev/null || skip "Cannot find sha256sum, test skipped"
+modprobe dm-crypt >/dev/null 2>&1 || 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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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
+sha256sum /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 | sha256sum > $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 | sha256sum > $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..38abcfb
--- /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."
+command -v dmsetup >/dev/null || skip "Cannot find dmsetup, test skipped"
+command -v keyctl >/dev/null || skip "Cannot find keyctl, test skipped"
+modprobe dm-crypt >/dev/null 2>&1 || 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..fdb4cd3
--- /dev/null
+++ b/tests/loopaes-test
@@ -0,0 +1,192 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+# 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()
+{
+ remove_mapping
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 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."
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+command -v uuencode >/dev/null || 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..18afcd5
--- /dev/null
+++ b/tests/luks1-compat-test
@@ -0,0 +1,124 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+TST_DIR=luks1-images
+MAP=luks1tst
+KEYFILE=keyfile1
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP
+ rm -rf $TST_DIR
+}
+
+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"
+ remove_mapping
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 remove_imgs()
+{
+ echo "WARNING: $1 not available, not testing some images."
+ rm $(ls $TST_DIR/*$1*.img)
+}
+
+function test_one()
+{
+ $CRYPTSETUP benchmark -c "$1" -s "$2" | grep -v "#" || remove_imgs $1
+}
+
+function test_required()
+{
+ echo "REQUIRED KDF TEST"
+ $CRYPTSETUP benchmark -h whirlpool | grep "N/A" && remove_imgs whirlpool
+
+ 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
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+command -v blkid >/dev/null || skip "blkid tool required, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+[ ! -d $TST_DIR ] && tar xJf $srcdir/luks1-images.tar.xz --no-same-owner
+test_required
+
+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."
+ remove_mapping
+ 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=$(blkid -p -o value -s UUID /dev/mapper/$MAP)
+ $CRYPTSETUP remove $MAP || fail
+ [ "$UUID" != "DEAD-BABE" ] && fail "UUID check failed."
+ echo " [OK]"
+done
+
+remove_mapping
+exit 0
diff --git a/tests/luks1-images.tar.xz b/tests/luks1-images.tar.xz
new file mode 100644
index 0000000..dd099c6
--- /dev/null
+++ b/tests/luks1-images.tar.xz
Binary files differ
diff --git a/tests/luks2-integrity-test b/tests/luks2-integrity-test
new file mode 100755
index 0000000..a8082f8
--- /dev/null
+++ b/tests/luks2-integrity-test
@@ -0,0 +1,203 @@
+#!/bin/bash
+#
+# Test cryptsetup/authenticated encryption compatibility.
+#
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+DEV_NAME=dmi_test
+DEV=mode-test.img
+HEADER_IMG=mode-test-detached.img
+PWD1=nHjJHjI23JK
+KEY_FILE=key.img
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+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 $HEADER_IMG >/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
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+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 [detached]
+{
+ if [ -n "$3" ]; then
+ PARAMS="$DEV_NAME --header $HEADER_IMG"
+ else
+ PARAMS="$DEV_NAME"
+ fi
+ X=$($CRYPTSETUP status $PARAMS | 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
+{
+ 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
+{
+ 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 [test_hdr]
+{
+ 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."
+
+ # check detached header activation
+ if [ -n "$8" ] ; then
+ echo -n "[DETACHED_HDR]"
+ $CRYPTSETUP luksHeaderBackup -q --header-backup-file $HEADER_IMG $DEV || fail
+ wipefs -a $DEV >/dev/null 2>&1 || fail
+ $CRYPTSETUP open --header $HEADER_IMG -d $KEY_FILE $DEV $DEV_NAME || fail "Cannot activate device."
+ status_check "cipher" $1 1
+ status_check "sector size" $6 1
+ status_check "integrity:" $3 1
+ status_check "keysize:" $(($4 + $5)) 1
+ [ $5 -gt 0 ] && status_check "integrity keysize:" $5 1
+ int_check_sum $1 $7
+ $CRYPTSETUP close $DEV_NAME || fail "Cannot deactivate device."
+ $CRYPTSETUP luksHeaderRestore -q --header-backup-file $HEADER_IMG $DEV || fail
+ rm -f $HEADER_IMG
+ fi
+
+ 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."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+modprobe dm-integrity >/dev/null 2>&1
+dmsetup targets | grep integrity >/dev/null 2>&1 || skip "Cannot find dm-integrity target, test skipped."
+command -v wipefs >/dev/null || skip "Cannot find wipefs, test skipped."
+
+add_device
+
+intformat aes-cbc-essiv:sha256 hmac-sha256 hmac\(sha256\) 128 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c 1
+intformat aes-xts-plain64 hmac-sha256 hmac\(sha256\) 256 256 512 ee501705a084cd0ab6f4a28014bcf62b8bfa3434de00b82743c50b3abf06232c 1
+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 1
+intformat aegis128-random aead aead 128 0 4096 358d6beceddf593aff6b22c31684e0df9c226330aff5812e060950215217d21b 1
+
+cleanup
diff --git a/tests/luks2-reencryption-mangle-test b/tests/luks2-reencryption-mangle-test
new file mode 100755
index 0000000..5aa62e4
--- /dev/null
+++ b/tests/luks2-reencryption-mangle-test
@@ -0,0 +1,548 @@
+#!/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_HDR_BCP=$IMG_HDR.bcp
+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_HDR_BCP $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()
+{
+ command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
+}
+
+function img_json_save()
+{
+ local _hdr=$IMG
+ [ -z "$1" ] || _hdr="$1"
+ # FIXME: why --json-file cannot be used?
+ $CRYPTSETUP luksDump --dump-json-metadata $_hdr | jq -c -M . | tr -d '\n' >$IMG_JSON
+}
+
+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
+ $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 _hdr="$IMG"
+ local LUKS2_BIN1_OFFSET=448
+ local LUKS2_BIN2_OFFSET=$((LUKS2_BIN1_OFFSET + $JSON_MSIZE))
+ local LUKS2_JSON_SIZE=$(($JSON_MSIZE - 4096))
+
+ # if present jq script, mangle JSON
+ if [ -n "$1" ]; then
+ local JSON=$(cat $IMG_JSON)
+ echo $JSON | jq -M -c "$1" >$IMG_JSON || fail
+ local JSON=$(cat $IMG_JSON)
+ echo $JSON | tr -d '\n' >$IMG_JSON || fail
+ fi
+
+ [ -z "$2" ] || _hdr="$2"
+
+ # wipe JSON areas
+ _dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
+ _dd if=/dev/zero of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))
+
+ # write JSON data
+ _dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=4096
+ _dd if=$IMG_JSON of=$_hdr count=$LUKS2_JSON_SIZE seek=$(($JSON_MSIZE + 4096))
+
+ # erase sha256 checksums
+ _dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN1_OFFSET
+ _dd if=/dev/zero of=$_hdr count=64 seek=$LUKS2_BIN2_OFFSET
+
+ # calculate sha256 and write chexksums
+ local SUM1_HEX=$(_dd if=$_hdr count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
+ echo $SUM1_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN1_OFFSET count=64 || fail
+
+ local SUM2_HEX=$(_dd if=$_hdr skip=$JSON_MSIZE count=$JSON_MSIZE | sha256sum | cut -d ' ' -f 1)
+ echo $SUM2_HEX | xxd -r -p | _dd of=$_hdr seek=$LUKS2_BIN2_OFFSET count=64 || fail
+
+ 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_dump_ok()
+{
+ $CRYPTSETUP luksDump $IMG >/dev/null || fail
+ img_check_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()
+{
+ $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS -q --disable-locks --force-offline-reencrypt --resilience none || fail
+}
+
+function img_run_reenc_ok_data_shift()
+{
+ $CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS -q --disable-locks --force-offline-reencrypt || fail
+}
+
+function img_run_reenc_fail()
+{
+$CRYPTSETUP_RAW reencrypt $IMG $CS_PWPARAMS --force-offline-reencrypt --disable-locks -q 2>/dev/null && fail "Reencryption passed (should have failed)."
+img_hash_unchanged
+}
+
+function img_check_fail_repair()
+{
+ 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
+}
+
+function img_check_fail_repair_ok()
+{
+ img_check_fail_repair
+ img_run_reenc_ok
+}
+
+function img_check_fail_repair_ok_data_shift()
+{
+ img_check_fail_repair
+ img_run_reenc_ok_data_shift
+}
+
+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()
+{
+ export INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}"
+ $CRYPTSETUP_RAW "$@"
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+
+bin_check jq
+bin_check sha256sum
+bin_check xxd
+
+export LANG=C
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+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
+
+# Simulate future version of reencrypt flag (should pass luksDump)
+img_prepare
+img_update_json '.config.requirements.mandatory = ["online-reencrypt-v999"]'
+img_check_dump_ok
+
+# Multiple reencrypt requirement flags makes LUKS2 invalid
+img_prepare
+img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt-v999"]'
+img_check_fail
+
+img_prepare
+img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt"]'
+img_check_fail
+
+# just regular unknown requirement
+img_prepare
+img_update_json '.config.requirements.mandatory = .config.requirements.mandatory + ["online-reencrypt-v3X"]'
+img_check_dump_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_data_shift
+
+#
+# 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
+
+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
+
+echo "[9] Decryption with datashift"
+img_prepare_raw
+$CRYPTSETUP reencrypt $CS_PARAMS --decrypt --init-only --force-offline-reencrypt --resilience checksum --header $IMG_HDR $IMG || fail
+cp $IMG_HDR $IMG_HDR_BCP
+
+# change hash
+img_json_save $IMG_HDR_BCP
+img_update_json '.keyslots."1".area.hash = "sha12345"' $IMG_HDR
+$CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
+
+# change sector size
+img_json_save $IMG_HDR_BCP
+img_update_json '.keyslots."1".area.sector_size = 1024' $IMG_HDR
+$CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
+
+# replace with new resilience mode
+img_json_save $IMG_HDR_BCP
+img_update_json 'del(.keyslots."1".area.hash) |
+ del(.keyslots."1".sector_size) |
+ .keyslots."1".area.type = "datashift-journal"' $IMG_HDR
+$CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
+
+# downgrade reencryption requirement
+img_json_save $IMG_HDR_BCP
+img_update_json '.config.requirements.mandatory = ["online-reencrypt-v2"]' $IMG_HDR
+$CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
+
+# change datashift value
+img_json_save $IMG_HDR_BCP
+img_update_json '.keyslots."1".area.shift_size = (((.keyslots."1".area.shift_size | tonumber) - 4096) | tostring)' $IMG_HDR
+$CRYPTSETUP reencrypt --header $IMG_HDR $IMG $CS_PARAMS --force-offline-reencrypt 2>/dev/null && fail
+
+remove_mapping
+exit 0
diff --git a/tests/luks2-reencryption-test b/tests/luks2-reencryption-test
new file mode 100755
index 0000000..a647a8c
--- /dev/null
+++ b/tests/luks2-reencryption-test
@@ -0,0 +1,2207 @@
+#!/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=$IMG.hdr
+HEADER_LUKS2_PV=blkid-luks2-pv.img
+IMG_FS=xfs_512_block_size.img
+KEY1=key1
+VKEY1=vkey1
+PWD1="93R4P4pIqAH8"
+PWD2="1cND4319812f"
+PWD3="1-9Qu5Ejfnqv"
+DEV_LINK="reenc-test-link"
+
+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 ]; 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 >/dev/null 2>&1
+ if [ -b "$1" ]; then
+ sleep .1
+ _tries=$((_tries-1))
+ fi
+ done
+
+ test ! -b "$1" || rmmod scsi_debug >/dev/null 2>&1
+}
+
+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_LINK $HEADER_LUKS2_PV $IMG_FS >/dev/null 2>&1
+ rmmod scsi_debug >/dev/null 2>&1
+ 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
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 1
+ DEV="/dev/"$(grep -l -e scsi_debug /sys/block/*/device/model | cut -f4 -d /)
+ [ -b $DEV ] || fail "Cannot find $DEV."
+}
+
+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_head() # $1 dev, $2 length (in MiBs)
+{
+ dd if=/dev/zero of=$1 bs=1M count=$2 conv=notrunc >/dev/null 2>&1
+}
+
+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
+ if [ $# -gt 2 ]; then
+ dd if=/dev/urandom of=$1 bs=1M seek=$2 conv=notrunc >/dev/null 2>&1
+ fi
+ else
+ local size=$(stat --printf="%s" $1)
+ truncate -s 0 $1
+ if [ $# -gt 2 ]; then
+ local diff=$((size-$2*1024*1024))
+ echo "size: $size, diff: $diff"
+ truncate -s $diff $1
+ # wipe_dev_head $1 $((diff/(1024*1024)))
+ dd if=/dev/urandom of=$1 bs=1M seek=$2 size=$((diff/(1024*1024))) conv=notrunc >/dev/null 2>&1
+ else
+ truncate -s $size $1
+ fi
+ 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=$(sha1sum $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_dev_head() # $1 dev, $2 len, $3 hash
+{
+ local hash=$(dd if=$1 bs=512 count=$2 2>/dev/null | sha1sum | cut -d' ' -f1)
+ [ $hash != "$3" ] && fail "HASH differs (expected: $3) (result $hash)"
+}
+
+function check_hash_head() # $1 pwd, $2 len, $3 hash, $4 hdr
+{
+ open_crypt $1 $4
+ check_hash_dev_head /dev/mapper/$DEV_NAME $2 $3
+ $CRYPTSETUP remove $DEV_NAME || fail
+}
+
+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 --force-offline-reencrypt -q $FAST_PBKDF_ARGON >/dev/null 2>&1 && fail
+ fix_writes $OVRDEV $OLD_DEV
+
+ echo $PWD1 | $CRYPTSETUP -q repair $DEV $_hdr || fail
+
+ check_hash $PWD1 $3 $4
+
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV $_hdr --resilience $2 --sector-size $1 -q $FAST_PBKDF_ARGON || fail
+ check_hash $PWD1 $3 $4
+
+ echo "[OK]"
+}
+
+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
+
+ [ -f $4 ] && rm -f $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
+
+ [ -f $4 ] && rm -f $4
+
+ 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
+
+ [ -f $4 ] && rm -f $4
+
+ 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
+
+ [ -f $4 ] && rm -f $4
+
+ echo "[OK]"
+}
+
+function decrypt_recover() { # $1 hash, $2 hdr, $3 dev size, $4 resilience, $5 hotzone size
+ local _res=""
+ local _maxhz=""
+ test -z "$4" || _res="--resilience $4"
+ test -z "$5" || _maxhz="--hotzone-size $5"
+ echo -n "[${4:-default}]"
+
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 --init-only $_maxhz >/dev/null || fail
+
+ error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
+ fix_writes $OVRDEV $OLD_DEV
+
+ echo $PWD1 | $CRYPTSETUP -q repair $DEV --header $2 || fail
+
+ $CRYPTSETUP luksDump $2 | grep -q "online-reencrypt"
+ if [ $? -eq 0 ]; then
+ check_hash $PWD1 $1 $2
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 $_res -q $FAST_PBKDF_ARGON || fail
+ fi
+
+ check_hash_dev_head $DEV $3 $1
+
+ [ -f $2 ] && rm -f $2
+
+ echo -n "[OK]"
+}
+
+function decrypt_recover_online() { # $1 hash, $2 hdr, $3 dev size
+ local _res=""
+ local _maxhz=""
+ test -z "$4" || _res="--resilience $4"
+ test -z "$5" || _maxhz="--hotzone-size $5"
+ echo -n "[${4:-default}]"
+
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 $_maxhz --init-only >/dev/null 2>&1 || fail
+
+ error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
+ $CRYPTSETUP status $DEV_NAME --header $2 | grep -q "reencryption: in-progress" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ fix_writes $OVRDEV $OLD_DEV
+
+ # recovery during activation
+ echo $PWD1 | $CRYPTSETUP open $DEV --header $2 $DEV_NAME || fail
+
+ check_hash_dev /dev/mapper/$DEV_NAME $1
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q || fail
+
+ $CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
+ check_hash_dev_head $DEV $3 $1
+
+ [ -f $2 ] && rm -f $2
+
+ echo -n "[OK]"
+}
+
+function decrypt_recover_online_moved() { # $1 hash, $2 hdr, $3 dev size
+ local _res=""
+ local _maxhz=""
+ test -z "$4" || _res="--resilience $4"
+ test -z "$5" || _maxhz="--hotzone-size $5"
+ echo -n "[${4:-default}]"
+
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --decrypt --header $2 $_maxhz $_res --init-only >/dev/null 2>&1 || fail
+
+ error_writes $OVRDEV $OLD_DEV $ERROFFSET $ERRLENGTH
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV --header $2 -q $_res >/dev/null 2>&1 && fail
+ $CRYPTSETUP status $DEV_NAME --header $2 | grep -q "reencryption: in-progress" || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+ fix_writes $OVRDEV $OLD_DEV
+
+ # recovery but activation fails due to last segment recovery makes it plaintext device
+ echo $PWD1 | $CRYPTSETUP open $DEV --header $2 $DEV_NAME 2>/dev/null && fail
+
+ $CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
+ check_hash_dev_head $DEV $3 $1
+
+ [ -f $2 ] && rm -f $2
+
+ echo -n "[OK]"
+}
+
+# sector size (bytes)
+# reenc dev size (sectors)
+# reenc dev digest
+# resilience
+# orig size
+# orig size digest
+# hdr (optional)
+function reencrypt_offline_fixed_size() {
+ local _esz=$(($1>>9))
+ local _hdr=""
+ # round-up fixed size to megabytes
+ local _mbs=$((($2>>11)+1))
+ test -z "$7" || _hdr="--header $7"
+
+ if [ -z "$7" ]; then
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 16384 $FAST_PBKDF_ARGON $DEV || fail
+ else
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $7 $FAST_PBKDF_ARGON $DEV || fail
+ fi
+ echo $PWD1 | $CRYPTSETUP open $_hdr $DEV $DEV_NAME || fail
+ wipe_dev_head /dev/mapper/$DEV_NAME $_mbs
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ # reencrypt with fixed device size
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --device-size $2s --resilience $4 --force-offline-reencrypt || fail
+
+ check_hash_head $PWD1 $2 $3 $7
+ wipe $PWD1 $7
+
+ # try to reencrypt device size + 1 encryption sector size
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --sector-size $1 --init-only --force-offline-reencrypt || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail
+ check_hash $PWD1 $6 $7
+
+ # misaligned reencryption size
+ if [ $_esz -gt 1 ]; then
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV $_hdr --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail
+ $CRYPTSETUP luksDump ${7:-$DEV} | grep -q "2: crypt" || fail
+ $CRYPTSETUP luksDump ${7:-$DEV} | grep -q "3: crypt" && fail
+ check_hash $PWD1 $6 $7
+ fi
+}
+
+# sector size (bytes)
+# reenc dev size (sectors)
+# reenc dev digest
+# resilience
+# orig size
+# orig size digest
+# hdr
+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
+ [ -f $7 ] && rm -f $7
+
+ # try to reencrypt device size + 1 encryption sector size
+ wipe_dev $DEV
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --device-size $(($5+_esz))s --resilience $4 2>/dev/null && fail
+ check_hash $PWD1 $6 $7
+
+ # misaligned reencryption size
+ if [ $_esz -gt 1 ]; then
+ [ -f $7 ] && rm -f $7
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -q $FAST_PBKDF_ARGON $DEV --header $7 --sector-size $1 --init-only || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV --header $7 --device-size $(($2+_esz-1))s --resilience $4 2>/dev/null && fail
+ $CRYPTSETUP luksDump $7 | grep -q "2: crypt" || fail
+ $CRYPTSETUP luksDump $7 | grep -q "3: crypt" && fail
+ check_hash $PWD1 $6 $7
+ fi
+
+ [ -f $7 ] && rm -f $7
+}
+
+# sector size (bytes)
+# reenc dev size (sectors)
+# reenc dev digest
+# resilience
+# orig size
+# orig size digest
+# hdr
+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
+
+ [ -n "$7" -a -f "$7" ] && rm -f $7
+}
+
+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 check_blkid() {
+ bin_check blkid
+ xz -dkf $HEADER_LUKS2_PV.xz
+ if ! $($CRYPTSETUP --version | grep -q "BLKID"); then
+ HAVE_BLKID=0
+ elif $(blkid -p -n crypto_LUKS $HEADER_LUKS2_PV >/dev/null 2>&1); then
+ HAVE_BLKID=1
+ xz -dkf $IMG_FS.xz
+ blkid $IMG_FS | grep -q BLOCK_SIZE && BLKID_BLOCK_SIZE_SUPPORT=1
+ else
+ HAVE_BLKID=0
+ fi
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 bin_check()
+{
+ command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+fips_mode && skip "This test cannot be run in FIPS mode."
+modprobe --dry-run scsi_debug >/dev/null 2>&1 || skip "This kernel seems to not support proper scsi_debug module, test skipped."
+modprobe dm-crypt >/dev/null 2>&1 || fail "dm-crypt failed to load"
+modprobe dm-delay > /dev/null 2>&1
+dm_crypt_features
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ TEST_SECTORS="512 4096"
+else
+ TEST_SECTORS="512"
+fi
+
+modinfo scsi_debug -p | grep -q opt_xferlen_exp && OPT_XFERLEN_EXP="opt_xferlen_exp=6"
+
+export LANG=C
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+# REENCRYPTION tests
+
+# 28 MiBs of zeros (32MiBs - 4MiB LUKS2 header)
+HASH1=4da90c0638bd7d29ce3d0ace3df5ee99706c23da
+# 1 MiB of zeros
+HASH2=3b71f43ff30f4b15b5cd85dd9e95ebc7e84eb5a3
+# 256 MiBs of zeros
+HASH3=7b91dbdc56c5781edf6c8847b4aa6965566c5c75
+# 64 MiBs of zeroes
+HASH4=44fac4bedde4df04b9572ac665d3ac2c5cd00c7d
+# 56 MiBs of zeroes
+HASH5=bcd8ce9b30a43b2dacdf479493c93e167ef60946
+# 43 MiBs of zeroes
+HASH6=2cf8a5f40a2ab5373c5425d6071da480f1ce08e8
+# 31 MiBs of zeroes
+HASH7=7ed56dd14d2841cf169fe503d097be04192666bd
+# 60 MiBs of zeroes
+HASH8=233ba936226a3ac499e67babaebd0d4aafb9761a
+# 240 MiBs of zeroes (256MiBs - 16MiBs default LUKS2 header size)
+HASH9=045eebed703cce308e049deb019b877f0445862f
+# 16 MiBs of zeroes
+HASH10=3b4417fc421cee30a9ad0fd9319220a8dae32da2
+
+prepare dev_size_mb=32
+setup_luks2_env
+
+# Check that we can use other ciphers than AES in userspace backend.
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c twofish-xts-plain64 $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>/dev/null || skip "Cannot use Twofish cipher, test skipped"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c serpent-xts-plain64 $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>/dev/null || skip "Cannot use Serpent cipher, test skipped."
+wipe_dev $DEV
+
+echo "[1] Reencryption"
+echo -n "[512 sector]"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON 2>&1 | tail -1 | grep -q "not supported" && skip " No reenryption support, test skipped."
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+# simple test --active-name can consume absolute path to mapping
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -c aes-xts-plain64 --init-only $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --active-name /dev/mapper/$DEV_NAME --resilience none -q || fail
+XTS_KEY=$($CRYPTSETUP status $DEV_NAME | grep "keysize:" | sed 's/\( keysize: \)\([0-9]\+\)\(.*\)/\2/')
+[ "$XTS_KEY" -eq "$DEF_XTS_KEY" ] || fail "xts mode has wrong key size after reencryption ($XTS_KEY != expected $DEF_XTS_KEY)"
+echo $PWD1 | $CRYPTSETUP close $DEV_NAME || fail
+echo -n "[OK][4096 sector]"
+prepare sector_size=4096 dev_size_mb=32
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 || fail
+ check_hash $PWD1 $HASH1
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail
+ check_hash $PWD1 $HASH1
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail
+ check_hash $PWD1 $HASH1
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail
+ check_hash $PWD1 $HASH1
+fi
+echo -n "[OK][4096/512 sector]"
+prepare sector_size=512 physblk_exp=3 dev_size_mb=32
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+echo "[OK]"
+
+# reencrypt minimal device size (FIXME: change data device size to single encryption sector size)
+# temporary small device size is default luks2 hdr size + 1MiB
+echo -n "[small device reencryption]"
+prepare dev_size_mb=5
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+check_hash $PWD1 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH2
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON --sector-size 4096 --force-offline-reencrypt || fail
+ check_hash $PWD1 $HASH2
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 256 -c twofish-cbc-essiv:sha256 --resilience journal --sector-size 2048 $FAST_PBKDF_ARGON || fail
+ check_hash $PWD1 $HASH2
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience none $FAST_PBKDF_ARGON --sector-size 1024 || fail
+ check_hash $PWD1 $HASH2
+ echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -s 128 -c aes-cbc-essiv:sha256 --resilience checksum --sector-size 512 $FAST_PBKDF_ARGON || fail
+ check_hash $PWD1 $HASH2
+fi
+echo "[OK]"
+
+echo "[2] Encryption with data shift"
+# well, movin' zeroes :-)
+preparebig 64
+wipe_dev $DEV
+check_hash_dev $DEV $HASH4
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON || fail
+check_hash_head $PWD1 $((56*1024*2)) $HASH5
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail
+check_hash_head $PWD1 $((43*1024*2)) $HASH6
+wipe_dev $DEV
+# offset 21504 equals 10,5MiBs, equals --reduce-device-size 21M from test above (30M is ignored here, we'll reduce it to 21M in cryptsetup anyway)
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c twofish-cbc-essiv:sha256 -s 128 --offset 21504 --reduce-device-size 30M -q $FAST_PBKDF_ARGON > /dev/null || fail
+check_hash_head $PWD1 $((43*1024*2)) $HASH6
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 33M -q $FAST_PBKDF_ARGON || fail
+check_hash_head $PWD1 $((31*1024*2)) $HASH7
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M -q $FAST_PBKDF_ARGON > /dev/null 2>&1 && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --reduce-device-size 8M --init-only -q $FAST_PBKDF_ARGON $DEV || fail
+resize_file $DEVBIG -512
+echo $PWD1 | $CRYPTSETUP reencrypt -q $DEV 2> /dev/null && fail
+resize_file $DEVBIG 512
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --offset 32760 --reduce-device-size 8M -q $FAST_PBKDF_ARGON --init-only >/dev/null 2>&1 && fail
+# data offset at 21MiB
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --header $IMG_HDR --offset 43008 --reduce-device-size 21M -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH6 $IMG_HDR
+$CRYPTSETUP luksHeaderRestore --header-backup-file $IMG_HDR $DEV -q || fail
+check_hash $PWD1 $HASH6
+
+# Device activation after encryption initialization
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 -S11 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
+$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH5
+echo $PWD1 | $CRYPTSETUP reencrypt --resume-only $DEV -q || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH5
+
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP close $DEV_NAME
+check_hash_head $PWD1 $((56*1024*2)) $HASH5
+
+# Device activation using key file
+wipe_dev $DEV
+echo -n $PWD1 > $KEY1
+$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
+$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
+$CRYPTSETUP close $DEV_NAME
+echo $PWD1 | $CRYPTSETUP open $DEV --test-passphrase || fail
+
+# Small device encryption test
+preparebig 65
+# wipe only 1st MiB (final data size after encryption)
+wipe_dev $DEV 1
+check_hash_dev_head $DEV 2048 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M -q $FAST_PBKDF_ARGON || fail
+check_hash_head $PWD1 2048 $HASH2
+
+wipe_dev_head $DEV 1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt --reduce-device-size 64M --init-only -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
+check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q || fail
+check_hash_dev_head /dev/mapper/$DEV_NAME 2048 $HASH2
+
+echo "[3] Encryption with detached header"
+preparebig 256
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+wipe_dev $DEV
+rm -f $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+wipe_dev $DEV
+rm -f $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+wipe_dev $DEV
+rm -f $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+rm -f $IMG_HDR
+
+# Device activation after encryption initialization
+wipe_dev $DEV
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV $DEV_NAME >/dev/null || fail
+$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH3
+echo $PWD1 | $CRYPTSETUP reencrypt --resume-only --header $IMG_HDR --active-name $DEV_NAME -q || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH3
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --encrypt -c aes-cbc-essiv:sha256 -s 128 --reduce-device-size 8M -q $FAST_PBKDF_ARGON $DEV_NAME 2>/dev/null && fail
+$CRYPTSETUP close $DEV_NAME
+check_hash $PWD1 $HASH3 $IMG_HDR
+rm -f $IMG_HDR
+
+# Device encryption with data offset set in detached header
+wipe_dev $DEV
+dd if=/dev/urandom of=$DEV bs=512 count=32768 >/dev/null 2>&1
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR --offset 32768 -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH9 $IMG_HDR
+rm -f $IMG_HDR
+
+# Device activation using key file
+wipe_dev $DEV
+echo -n $PWD1 > $KEY1
+$CRYPTSETUP reencrypt $DEV --encrypt --init-only -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR --key-file $KEY1 -q $FAST_PBKDF_ARGON $DEV_NAME >/dev/null || fail
+$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 || fail
+$CRYPTSETUP close $DEV_NAME
+echo $PWD1 | $CRYPTSETUP open --header $IMG_HDR $DEV --test-passphrase || fail
+
+# Encrypt without size reduction must not allow header device same as data device
+wipe_dev_head $DEV 1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --type luks2 --encrypt --header $DEV -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $DEV 2>/dev/null && fail
+ln -s $DEV $DEV_LINK || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --type luks2 --encrypt --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $DEV 2>/dev/null && fail
+rm -f $DEV_LINK || fail
+
+dd if=/dev/zero of=$IMG bs=4k count=1 >/dev/null 2>&1
+echo $PWD1 | $CRYPTSETUP reencrypt $IMG --type luks2 --encrypt --header $IMG -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail
+ln -s $IMG $DEV_LINK || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $IMG --type luks2 --encrypt --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail
+
+echo "[4] Reencryption with detached header"
+wipe $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --resilience journal --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -c twofish-cbc-essiv:sha256 -s 128 --resilience none --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -c serpent-xts-plain --resilience checksum --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+check_hash $PWD1 $HASH3 $IMG_HDR
+# trivial check for detached header misuse
+dd if=/dev/zero of=$IMG bs=4k count=1 >/dev/null 2>&1
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open $IMG $DEV_NAME --header $IMG_HDR || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME2 --header $IMG_HDR || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --header $IMG_HDR -q || fail
+# key description mismatch in active device
+echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME2 --header $IMG_HDR >/dev/null 2>&1 && fail
+# also check it can abort initialization in this case
+$CRYPTSETUP luksDump $IMG_HDR | grep -q "online-reencrypt" && fail
+$CRYPTSETUP close $DEV_NAME || fail
+$CRYPTSETUP close $DEV_NAME2 || fail
+
+echo "[5] Decryption with detached header"
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c aes-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --header $IMG_HDR $DEV || fail
+check_hash_dev $DEV $HASH3
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience journal --header $IMG_HDR $DEV || fail
+check_hash_dev $DEV $HASH3
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c twofish-cbc-essiv:sha256 -s 128 --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience none --header $IMG_HDR $DEV || fail
+check_hash_dev $DEV $HASH3
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR $DEV || fail
+check_hash_dev $DEV $HASH3
+
+# check deferred remove works as expected after decryption
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 --sector-size 512 -c serpent-xts-plain --header $IMG_HDR -q $FAST_PBKDF_ARGON $DEV || fail
+open_crypt $PWD1 $IMG_HDR
+dmsetup create $DEV_NAME2 --table "0 1 linear /dev/mapper/$DEV_NAME 0" || fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --decrypt --resilience checksum --header $IMG_HDR --active-name $DEV_NAME || fail
+$CRYPTSETUP status $DEV_NAME >/dev/null || fail
+dmsetup remove --retry $DEV_NAME2
+$CRYPTSETUP status $DEV_NAME >/dev/null 2>&1 && fail
+
+# check tool can block some funny user ideas
+preparebig 64
+ln -s $DEV $DEV_LINK || fail
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 -c serpent-xts-plain -q $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $DEV -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $DEV_LINK -q 2>/dev/null && fail
+open_crypt $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME --header $DEV -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --active-name $DEV_NAME --header $DEV_LINK -q 2>/dev/null && fail
+$CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" && fail
+$CRYPTSETUP close $DEV_NAME
+
+# yet another funny idea
+rm -f $IMG_HDR
+$CRYPTSETUP luksHeaderBackup --header-backup-file $IMG_HDR $DEV || fail
+chmod +w $IMG_HDR || fail
+command -v wipefs >/dev/null && {
+ wipefs -a $DEV >/dev/null 2>&1 || fail
+}
+open_crypt $PWD1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --active-name $DEV_NAME --decrypt --header $IMG_HDR -q 2>/dev/null && fail
+$CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" && fail
+$CRYPTSETUP close $DEV_NAME || fail
+
+if ! dm_delay_features; then
+ echo "dm-delay target is missing, skipping recovery tests."
+ remove_mapping
+ exit 0
+fi
+
+echo "[6] Reencryption recovery"
+# (check opt-io size optimization in reencryption code does not affect recovery)
+# device with opt-io size 32k
+prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP
+OFFSET=8192
+
+echo "sector size 512->512"
+
+get_error_offsets 32 $OFFSET
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover 512 checksum $HASH1
+reencrypt_recover 512 journal $HASH1
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH1
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+ reencrypt_recover 4096 journal $HASH1
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH1
+ reencrypt_recover 4096 journal $HASH1
+fi
+
+echo "[7] Reencryption recovery (online i/o error)"
+
+echo "sector size 512->512"
+
+get_error_offsets 32 $OFFSET
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover_online 512 checksum $HASH1
+reencrypt_recover_online 512 journal $HASH1
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH1
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+ reencrypt_recover_online 4096 journal $HASH1
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 --sector-size 4096 -c aes-cbc-essiv:sha256 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH1
+ reencrypt_recover_online 4096 journal $HASH1
+fi
+
+echo "[8] Reencryption with detached header recovery"
+prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP
+
+echo "sector size 512->512"
+
+get_error_offsets 31 0
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+check_hash $PWD1 $HASH7 $IMG_HDR
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover 512 checksum $HASH7 $IMG_HDR
+reencrypt_recover 512 journal $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 31 0 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH7 $IMG_HDR
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+ reencrypt_recover 4096 journal $HASH7 $IMG_HDR
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 31 0 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH7 $IMG_HDR
+ reencrypt_recover 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[9] Reencryption with detached header recovery (online i/o error)"
+
+echo "sector size 512->512"
+
+get_error_offsets 31 0
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1 $IMG_HDR
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover_online 512 checksum $HASH7 $IMG_HDR
+reencrypt_recover_online 512 journal $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 31 0 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+ reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 31 0 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1 $IMG_HDR
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH7 $IMG_HDR
+ reencrypt_recover_online 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[10] Encryption recovery"
+prepare_linear_dev 64
+OFFSET=$((2*1024*2))
+
+echo "sector size 512"
+
+get_error_offsets 64 $OFFSET 512 $((62*1024*2))
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+encrypt_recover 512 4M $HASH8 $((60*1024*2)) $HASH4
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 4096"
+
+ get_error_offsets 64 $OFFSET 4096 $((62*1024*2))
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ encrypt_recover 4096 4M $HASH8 $((60*1024*2)) $HASH4
+fi
+
+echo "[11] Encryption recovery (online i/o error)"
+
+echo "sector size 512"
+
+get_error_offsets 64 $OFFSET 512 $((62*1024*2))
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+encrypt_recover_online 512 4M $HASH8 $((60*1024*2)) $HASH4
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 4096"
+
+ get_error_offsets 64 $OFFSET 4096 $((62*1024*2))
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ encrypt_recover_online 4096 4M $HASH8 $((60*1024*2)) $HASH4
+fi
+
+echo "[12] Encryption with detached header recovery"
+prepare_linear_dev 31 opt_blks=64 $OPT_XFERLEN_EXP
+
+get_error_offsets 31 0
+
+echo "sector size 512"
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+encrypt_recover_detached 512 checksum $HASH7 $IMG_HDR
+encrypt_recover_detached 512 journal $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ get_error_offsets 31 0 4096
+
+ echo "sector size 4096"
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ encrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR
+ encrypt_recover_detached 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[13] Encryption with detached header recovery (online i/o error)"
+
+get_error_offsets 31 0
+
+echo "sector size 512"
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+encrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR
+encrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ get_error_offsets 31 0 4096
+
+ echo "sector size 4096"
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ encrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR
+ encrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[14] Decryption with detached header recovery"
+
+echo "sector size 512"
+
+# TODO: What should decryption do when it finishes decryption during recovery (with open)
+get_error_offsets 31 2049
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+decrypt_recover_detached 512 journal $HASH7 $IMG_HDR
+decrypt_recover_detached 512 checksum $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 4096"
+
+ # TODO: What should decryption do when it finishes decryption during recovery (with open)
+ get_error_offsets 31 2048 4096
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ decrypt_recover_detached 4096 checksum $HASH7 $IMG_HDR
+ decrypt_recover_detached 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[15] Decryption with detached header recovery (online i/o error)"
+
+echo "sector size 512"
+
+# TODO: What should decryption do when it finishes decryption during recovery (with open)
+get_error_offsets 31 2049
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+decrypt_recover_detached_online 512 journal $HASH7 $IMG_HDR
+decrypt_recover_detached_online 512 checksum $HASH7 $IMG_HDR
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 4096"
+
+ # TODO: What should decryption do when it finishes decryption during recovery (with open)
+ get_error_offsets 31 2048 4096
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ decrypt_recover_detached_online 4096 checksum $HASH7 $IMG_HDR
+ decrypt_recover_detached_online 4096 journal $HASH7 $IMG_HDR
+fi
+
+echo "[16] Offline reencryption with fixed device size."
+preparebig 68
+
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ reencrypt_offline_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8
+ reencrypt_offline_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8
+ reencrypt_offline_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[17] Online reencryption with fixed device size."
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ reencrypt_online_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8
+ reencrypt_online_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8
+ reencrypt_online_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[18] Offline reencryption with fixed device size (detached header)."
+preparebig 60
+
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ reencrypt_offline_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ reencrypt_offline_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ reencrypt_offline_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[19] Online reencryption with fixed device size (detached header)."
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ reencrypt_online_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ reencrypt_online_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ reencrypt_online_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[20] Offline encryption with fixed device size (detached header)."
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ encrypt_offline_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ encrypt_offline_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ encrypt_offline_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[21] Offline decryption with fixed device size (detached header)."
+prepare_linear_dev 60
+for test_sector_size in $TEST_SECTORS; do
+printf "sector size %4s: " $test_sector_size
+for test_res in checksum journal none; do
+ echo -n "[$test_res]"
+ decrypt_offline_fixed_size $test_sector_size 2048 $HASH2 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ decrypt_offline_fixed_size $test_sector_size $((28*1024*2)) $HASH1 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ decrypt_offline_fixed_size $test_sector_size $((31*1024*2)) $HASH7 $test_res $((60*1024*2)) $HASH8 $IMG_HDR
+ echo -n "[OK]"
+done
+echo ""
+done
+
+echo "[22] Multi-keyslot device reencryption"
+prepare dev_size_mb=17
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --offset 32768 $FAST_PBKDF_ARGON $DEV || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV || fail
+echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo -e "$PWD1\n$PWD2\n$PWD3" | $CRYPTSETUP reencrypt $DEV -q || fail
+check_hash $PWD1 $HASH2
+check_hash $PWD2 $HASH2
+check_hash $PWD3 $HASH2
+
+# check at least pbkdf type is preserved
+$CRYPTSETUP luksDump $DEV | grep -e "3: luks2" -A5 | grep -q "argon2" || fail
+$CRYPTSETUP luksDump $DEV | grep -e "4: luks2" -A5 | grep -q "pbkdf2" || fail
+$CRYPTSETUP luksDump $DEV | grep -e "5: luks2" -A5 | grep -q "argon2" || fail
+
+echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV $KEY1 || fail
+
+# with more keyslots, specific has to be selected
+$CRYPTSETUP reencrypt $DEV -d $KEY1 -q 2>/dev/null && fail
+$CRYPTSETUP reencrypt $DEV -d $KEY1 -q -S0 || fail
+open_crypt
+check_hash_dev /dev/mapper/$DEV_NAME $HASH2
+$CRYPTSETUP close $DEV_NAME
+
+# there should be single keyslot now
+$CRYPTSETUP reencrypt $DEV -d $KEY1 -q || fail
+echo $PWD1 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S1 -d $KEY1 || fail
+
+echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S2 --unbound --key-size 32 || fail
+echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S22 --unbound --key-size 32 || fail
+echo $PWD3 | $CRYPTSETUP -q luksAddKey $FAST_PBKDF2 $DEV -S23 --unbound --key-size 32 || fail
+
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S1 -q || fail
+$CRYPTSETUP open --test-passphrase -d $KEY1 $DEV 2>/dev/null && fail
+echo $PWD3 | $CRYPTSETUP open --test-passphrase -S2 $DEV || fail
+echo $PWD3 | $CRYPTSETUP open --test-passphrase -S22 $DEV || fail
+check_hash $PWD1 $HASH2
+
+# fill 31 keyslots
+COUNT=27
+while [ $COUNT -gt 0 ]; do
+ echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail
+ COUNT=$((COUNT-1))
+done
+
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -S0 -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 30 || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 || fail
+
+COUNT=14
+while [ $COUNT -gt 0 ]; do
+ echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey $DEV -q $FAST_PBKDF_ARGON || fail
+ COUNT=$((COUNT-1))
+done
+
+echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 1 || fail
+# one wrong passphrase
+echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD2" | $CRYPTSETUP reencrypt $DEV -q 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --resume-only -q 2>/dev/null && fail
+echo -e "$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1\n$PWD1" | $CRYPTSETUP reencrypt $DEV -q || fail
+
+#test error path behaves as expected for initialization with not enough space in binary area
+# create LUKS2 header with keyslots binary space for exactly 4 keyslots
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --luks2-keyslots-size $((4*258048)) -S0 -s512 --cipher aes-xts-plain64 $FAST_PBKDF_ARGON $DEV >/dev/null || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -S1 $DEV -q $FAST_PBKDF_ARGON || fail
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP luksAddKey -S2 $DEV -q $FAST_PBKDF_ARGON || fail
+# there is not enough space in binary area for keyslot id 4 (replacement for id 2)
+echo -e "$PWD1\n$PWD2\n$PWD2" | $CRYPTSETUP reencrypt $DEV --init-only -q 2>/dev/null && fail
+$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
+# check cli removed all unbound keyslots created in-before reencryption initialization
+$CRYPTSETUP luksDump $DEV | grep -q "unbound" && fail
+
+echo $PWD1 | $CRYPTSETUP luksKillSlot $DEV 2 || fail
+# there is not enough space in binary area for reencryption keyslot
+echo -e "$PWD1\n$PWD2" | $CRYPTSETUP reencrypt $DEV --init-only -q 2>/dev/null && fail
+$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
+# check cli removed all unbound keyslots created in-before reencryption initialization
+$CRYPTSETUP luksDump $DEV | grep -q "unbound" && fail
+
+echo "[23] Reencryption with specified new volume key"
+prepare dev_size_mb=32
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 256 -c aes-cbc-essiv:sha256 --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+echo -e "$PWD1\n$PWD3" | $CRYPTSETUP -q luksAddKey $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q -S0 $FAST_PBKDF_ARGON --volume-key-file $VKEY1 -s 128 || fail
+check_hash $PWD1 $HASH1
+$CRYPTSETUP luksErase -q $DEV || fail
+echo $PWD1 | $CRYPTSETUP luksAddKey -q $FAST_PBKDF_ARGON --volume-key-file $VKEY1 -s 128 $DEV || fail
+check_hash $PWD1 $HASH1
+
+echo "[24] Reencryption with initial cipher_null"
+# aka custom encryption
+prepare dev_size_mb=32
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail
+check_hash $PWD1 $HASH1
+
+# online
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -c aes-xts-plain64 -q $FAST_PBKDF_ARGON || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH1
+if [ $HAVE_KEYRING -gt 0 ]; then
+ $CRYPTSETUP status $DEV_NAME | grep -q "key location: keyring" || fail
+fi
+$CRYPTSETUP close $DEV_NAME
+
+# simulate LUKS2 device with cipher_null in both keyslot and segment (it can be created only by up conversion from LUKS1)
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 -s 128 -c cipher_null-ecb --offset 8192 $FAST_PBKDF2 $DEV || fail
+$CRYPTSETUP convert -q --type luks2 $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q $FAST_PBKDF_ARGON >/dev/null || fail
+check_hash $PWD1 $HASH1
+# both keyslot and segment cipher must not be null after reencryption with default params
+$CRYPTSETUP luksDump $DEV | grep -q "cipher_null" && fail
+
+# multistep reencryption with initial cipher_null
+preparebig 64
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail
+$CRYPTSETUP close $DEV_NAME
+check_hash $PWD1 $HASH5
+
+echo $PWD1 | $CRYPTSETUP luksFormat --type luks2 $DEV -c null --offset 16384 -q $FAST_PBKDF_ARGON || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV --hotzone-size 1M --resilience none -q $FAST_PBKDF_ARGON >/dev/null || fail
+check_hash $PWD1 $HASH5
+
+echo "[25] Reencryption recovery with cipher_null"
+# (check opt-io size optimization in reencryption code does not affect recovery)
+# device with opt-io size 32k
+prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP
+OFFSET=8192
+
+echo "sector size 512->512"
+
+get_error_offsets 32 $OFFSET
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover 512 checksum $HASH1
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+reencrypt_recover 512 journal $HASH1
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH1
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+ reencrypt_recover 4096 journal $HASH1
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover 4096 checksum $HASH1
+ reencrypt_recover 4096 journal $HASH1
+fi
+
+echo "[26] Reencryption recovery with cipher_null (online i/o error)"
+
+echo "sector size 512->512"
+
+get_error_offsets 32 $OFFSET
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+reencrypt_recover_online 512 checksum $HASH1
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 -c null --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+reencrypt_recover_online 512 journal $HASH1
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo "sector size 512->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH1
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+ reencrypt_recover_online 4096 journal $HASH1
+
+ echo "sector size 4096->4096"
+
+ get_error_offsets 32 $OFFSET 4096
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -c null --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ reencrypt_recover_online 4096 checksum $HASH1
+ reencrypt_recover_online 4096 journal $HASH1
+fi
+
+echo "[27] Verify test passphrase mode works with reencryption metadata"
+echo $PWD1 | $CRYPTSETUP -S5 -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV || fail
+echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey --unbound -s80 -S0 $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --init-only $DEV || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $DEV || fail
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat -S5 --header $IMG_HDR --type luks2 $FAST_PBKDF_ARGON $DEV || fail
+echo -e "$PWD1\n$PWD1" | $CRYPTSETUP luksAddKey --unbound -s80 -S0 $FAST_PBKDF_ARGON $IMG_HDR || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --init-only --header $IMG_HDR $DEV || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $IMG_HDR || fail
+rm -f $IMG_HDR
+wipe_dev_head $DEV 1
+
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --init-only --header $IMG_HDR $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $IMG_HDR || fail
+
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --reduce-device-size 8M $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open --test-passphrase $DEV || fail
+
+echo "[28] Prevent nested encryption"
+prepare_linear_dev 32 opt_blks=64 $OPT_XFERLEN_EXP
+
+#device already LUKS2
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
+
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --reduce-device-size 2m $FAST_PBKDF2 $DEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+test -f $IMG_HDR && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --reduce-device-size 2m $FAST_PBKDF2 $DEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+test -f $IMG_HDR && fail
+#type mismatch
+echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 $DEV 2>/dev/null && fail
+wipe_dev $DEV
+
+#detached header already LUKS2
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
+
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 --header $IMG_HDR $DEV 2>/dev/null && fail
+rm -f $IMG_HDR
+
+#data device already in reencryption
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --init-only $FAST_PBKDF_ARGON $DEV || fail
+
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+test -f $IMG_HDR && fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+test -f $IMG_HDR && fail
+#type mismatch
+echo $PWD1 | $CRYPTSETUP reencrypt -q --type luks1 $DEV 2>/dev/null && fail
+wipe_dev $DEV
+rm -f $IMG_HDR
+
+#header in reencryption (type mismatch)
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt -q --encrypt --type luks1 --header $IMG_HDR $FAST_PBKDF2 $DEV 2>/dev/null && fail
+
+echo "[29] Conflicting reencryption parameters"
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --init-only $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --reduce-device-size 4M $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-checksum 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-journal 2> /dev/null && fail
+wipe_dev_head $DEV 1
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --init-only --reduce-device-size 16M $DEV -q $FAST_PBKDF_ARGON 2> /dev/null || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience journal 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-checksum 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --resilience datashift-journal 2> /dev/null && fail
+wipe_dev_head $DEV 1
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --resilience datashift-checksum 2>/dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --resilience datashift-journal 2>/dev/null && fail
+rm -f $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --encrypt --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --header $IMG_HDR $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only $FAST_PBKDF_ARGON || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --header $IMG_HDR $DEV -q $FAST_PBKDF_ARGON 2> /dev/null && fail
+rm -f $IMG_HDR
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF2 $DEV || fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience datashift 2> /dev/null && fail
+test -f $IMG_HDR && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience none 2> /dev/null && fail
+test -f $IMG_HDR && fail
+$CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --init-only $FAST_PBKDF_ARGON --resilience checksum --hotzone-size 4m || fail
+$CRYPTSETUP isLuks $DEV -q && fail
+# $CRYPTSETUP luksDump $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience datashift 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience none 2> /dev/null && fail
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt $DEV --header $IMG_HDR -q --resilience journal || fail
+rm -f $IMG_HDR
+
+check_blkid
+if [ "$HAVE_BLKID" -gt 0 ]; then
+ echo "[30] Prevent nested encryption of broken LUKS device"
+ rm -f $IMG_HDR
+ xz -dkf $HEADER_LUKS2_PV.xz
+ wipe_dev $DEV
+
+ # broken header
+ echo $PWD1 | $CRYPTSETUP reencrypt -q --header $HEADER_LUKS2_PV $DEV $FAST_PBKDF_ARGON --encrypt --type luks2 2>/dev/null && fail
+ $CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
+ # broken device
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $HEADER_LUKS2_PV $FAST_PBKDF_ARGON --encrypt --force-offline-reencrypt --type luks2 --reduce-device-size 8m 2>/dev/null && fail
+ $CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
+ # broken data device only
+ echo $PWD1 | $CRYPTSETUP reencrypt -q --header $IMG_HDR $HEADER_LUKS2_PV $FAST_PBKDF_ARGON --encrypt --force-offline-reencrypt --type luks2 2>/dev/null && fail
+ test -f $IMG_HDR && fail
+fi
+
+if [ -n "$DM_SECTOR_SIZE" -a $HAVE_BLKID -gt 0 ]; then
+ echo "[31] Prevent dangerous sector size increase"
+ preparebig 64
+ echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 512 --type luks2 $FAST_PBKDF_ARGON $DEV || fail
+
+ # block encryption sector size increase on offline device
+ echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
+ $CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
+ $CRYPTSETUP luksDump $DEV | grep -q "online-reencrypt" && fail
+ $CRYPTSETUP luksDump $DEV | grep -q "sector: 1024" && fail
+
+ # --force-offline-reencrypt can bypass the constraint
+ echo $PWD1 | $CRYPTSETUP reencrypt --force-offline-reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV || fail
+ # resume must work
+ echo $PWD1 | $CRYPTSETUP reencrypt -q $FAST_PBKDF_ARGON $DEV || fail
+
+ # online with no superblock is fine
+ echo $PWD1 | $CRYPTSETUP open -q $DEV $DEV_NAME || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 4096 $FAST_PBKDF_ARGON $DEV || fail
+ $CRYPTSETUP close $DEV_NAME || fail
+
+ # sector size decrease is ok
+ echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 4096 --type luks2 $FAST_PBKDF_ARGON $DEV || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV || fail
+
+ if [ -n "$BLKID_BLOCK_SIZE_SUPPORT" ]; then
+ xz -dkf $IMG_FS.xz
+ # encryption checks must work in offline mode
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --header $IMG_HDR $IMG_FS $FAST_PBKDF_ARGON --init-only --type luks2 2>/dev/null && fail
+ test -f $IMG_HDR && fail
+
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --header $IMG_HDR $IMG_FS $FAST_PBKDF_ARGON --type luks2 2>/dev/null && fail
+ test -f $IMG_HDR && fail
+
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --reduce-device-size 8m $IMG_FS $FAST_PBKDF_ARGON --init-only --type luks2 2>/dev/null && fail
+ $CRYPTSETUP isLuks $IMG_FS && fail
+ echo $PWD1 | $CRYPTSETUP reencrypt --encrypt --force-offline-reencrypt --sector-size 1024 -q --reduce-device-size 8m $IMG_FS $FAST_PBKDF_ARGON --type luks2 2>/dev/null && fail
+ $CRYPTSETUP isLuks $IMG_FS && fail
+
+ echo $PWD1 | $CRYPTSETUP luksFormat -q --sector-size 512 --type luks2 $FAST_PBKDF_ARGON $DEV || fail
+ echo $PWD1 | $CRYPTSETUP open -q $DEV $DEV_NAME || fail
+ dd if=$IMG_FS of=/dev/mapper/$DEV_NAME bs=1M >/dev/null 2>&1
+
+ echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" && fail
+ echo $PWD1 | $CRYPTSETUP reencrypt --init-only -q --sector-size 1024 --active-name $DEV_NAME $FAST_PBKDF_ARGON 2>/dev/null && fail
+ $CRYPTSETUP status $DEV_NAME | grep -q "reencryption: in-progress" && fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 $FAST_PBKDF_ARGON $DEV 2>/dev/null && fail
+ $CRYPTSETUP luksDump $DEV | grep -q "sector: 512" || fail
+ echo $PWD1 | $CRYPTSETUP reencrypt -q --sector-size 1024 --active-name $DEV_NAME $FAST_PBKDF_ARGON 2>/dev/null && fail
+ $CRYPTSETUP luksDump $DEV | grep -q "sector: 512" || fail
+ fi
+fi
+
+echo "[32] Removal of encryption (LUKS2 legacy cryptsetup-reencrypt test)."
+prepare dev_size_mb=32
+OFFSET=8192
+
+# offline decryption with shift
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 57344 $HASH1
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+# online decryption with shift
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 57344 $HASH1
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+# offline decryption (separate initialization and decryption steps)
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
+check_hash $PWD1 $HASH1 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 57344 $HASH1
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+# online decryption with shift
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 57344 $HASH1
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+# same tests just with date size == LUKS2 header size
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
+wipe $PWD1
+check_hash $PWD1 $HASH10
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 32768 $HASH10
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 32768 $HASH10
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
+check_hash $PWD1 $HASH10 $IMG_HDR
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 32768 $HASH10
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 32768 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH10
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 32768 $HASH10
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+# 1MiB data size
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 63488 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+# --hotzone-size larger than data expected to get auto corrected by library
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --init-only --hotzone-size 4M || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 2048 $HASH2
+rm -f $IMG_HDR
+
+# small device (less than header size)
+prepare dev_size_mb=5
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S5 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 2048 $HASH2
+# FIXME: Should not reencryption remove it automatically?
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 -S5 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR || fail
+check_hash_dev_head $DEV 2048 $HASH2
+rm -f $IMG_HDR
+
+# initialization by --active-name parameter
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --active-name $DEV_NAME || fail
+check_hash_dev_head $DEV 2048 $HASH2
+rm -f $IMG_HDR
+
+# initialization and resume by --active-name parameter
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 $FAST_PBKDF_ARGON $DEV --offset 8192 || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --decrypt --header $IMG_HDR --active-name $DEV_NAME --init-only || fail
+check_hash_dev /dev/mapper/$DEV_NAME $HASH2
+echo $PWD1 | $CRYPTSETUP reencrypt $DEV -q --header $IMG_HDR --active-name $DEV_NAME || fail
+check_hash_dev_head $DEV 2048 $HASH2
+rm -f $IMG_HDR
+
+echo "[33] Decryption with datashift recovery (error in shift area)."
+prepare_linear_dev 32
+echo "sector size 512"
+
+# avoid error in moved segment area on purpose
+# Also do not create write error in last segment because
+# that would not trigger reencryption crash (read would pass)
+get_error_offsets 32 $OFFSET 512 $((32-1024*2-$OFFSET))
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+echo -n "resilience:"
+decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo -e "\nsector size 4096"
+
+ get_error_offsets 32 $OFFSET 4096 $((32-1024*2-$OFFSET))
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ echo -n "resilience:"
+ decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
+fi
+echo ""
+
+echo "[34] Decryption with datashift recovery (error in moved segment)."
+echo "sector size 512"
+
+HZ_SIZE=$((3*1024*2))
+
+# move injected error in moved segment area
+get_error_offsets 32 0 512 $HZ_SIZE
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+
+echo -n "resilience:"
+for res in datashift-journal datashift-checksum; do
+
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
+done
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo -e "\nsector size 4096"
+
+ # move injected error in moved segment area
+ get_error_offsets 32 0 4096 $HZ_SIZE
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+
+ echo -n "resilience:"
+ for res in datashift-journal datashift-checksum; do
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ wipe $PWD1
+
+ decrypt_recover $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
+ done
+fi
+echo ""
+
+echo "[35] Decryption with datashift recovery (online i/o error in shift area)."
+echo "sector size 512"
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+wipe_dev /dev/mapper/$DEV_NAME
+
+# avoid error in moved segment area on purpose
+# Also do not create write error in last segment because
+# that would not trigger reencryption crash (read would pass)
+get_error_offsets 32 $OFFSET 512 $((32-1024*2-$OFFSET))
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+
+echo -n "resilience:"
+decrypt_recover_online $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo -e "\nsector size 4096"
+
+ get_error_offsets 32 $OFFSET 4096 $((32-1024*2-$OFFSET))
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ wipe_dev /dev/mapper/$DEV_NAME
+
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+ echo -n "resilience:"
+ decrypt_recover_online $HASH1 $IMG_HDR $((32*1024*2-$OFFSET))
+fi
+echo ""
+
+echo "[36] Decryption with datashift recovery (online i/o error in moved segment)."
+echo "sector size 512"
+
+# move injected error in moved segment area
+get_error_offsets 32 0 512 $HZ_SIZE
+echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+
+echo -n "resilience:"
+for res in datashift-journal datashift-checksum; do
+
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ wipe_dev /dev/mapper/$DEV_NAME
+
+ decrypt_recover_online_moved $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
+done
+
+if [ -n "$DM_SECTOR_SIZE" ]; then
+ echo -e "\nsector size 4096"
+
+ get_error_offsets 32 0 4096 $HZ_SIZE
+ echo "ERR writes to sectors [$ERROFFSET,$(($ERROFFSET+$ERRLENGTH-1))]"
+
+ echo -n "resilience:"
+ for res in datashift-journal datashift-checksum; do
+
+ echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 4096 --offset $OFFSET $FAST_PBKDF_ARGON $DEV || fail
+ echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME || fail
+ wipe_dev /dev/mapper/$DEV_NAME
+
+ decrypt_recover_online_moved $HASH1 $IMG_HDR $((32*1024*2-$OFFSET)) $res $(($HZ_SIZE*512))
+ done
+fi
+echo ""
+
+echo "[37] Decryption with datashift (large data offsets)"
+prepare_linear_dev 512
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1015808 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
+check_hash_dev_head $DEV $((16*1024*2)) $HASH10
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1015808 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
+check_hash_dev_head $DEV $((16*1024*2)) $HASH10
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1046528 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
+wipe $PWD1
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
+check_hash_dev_head $DEV 2048 $HASH2
+rm -f $IMG_HDR
+
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks2 --sector-size 512 --offset 1046528 --luks2-keyslots-size 16M $FAST_PBKDF_ARGON $DEV || fail
+echo $PWD1 | $CRYPTSETUP open $DEV $DEV_NAME
+wipe_dev /dev/mapper/$DEV_NAME
+echo $PWD1 | $CRYPTSETUP reencrypt --decrypt --header $IMG_HDR $DEV -q || fail
+check_hash_dev_head $DEV 2048 $HASH2
+
+remove_mapping
+exit 0
diff --git a/tests/luks2-validation-test b/tests/luks2-validation-test
new file mode 100755
index 0000000..cd9f0a6
--- /dev/null
+++ b/tests/luks2-validation-test
@@ -0,0 +1,249 @@
+#!/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
+ ret=$?
+ test $ret -ne 0 || return 1
+ test $ret -ne 139 || 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()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+command -v jq >/dev/null || 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 increased 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 increased 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 increased 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 increased 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 increased 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"
+RUN luks2-segment-crypt-empty-encryption.img "F" "Failed to detect empty encryption field"
+
+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"
+RUN luks2-metadata-size-invalid.img "F" "Invalid metadata size in secondary hdr not rejected"
+RUN luks2-metadata-size-invalid-secondary.img "F" "Invalid metadata size in secondary hdr not rejected"
+
+echo "[7] Test invalid metadata object property"
+RUN luks2-invalid-tokens.img "F" "Invalid tokens objects not rejected"
+RUN luks2-invalid-top-objects.img "F" "Invalid top-level objects not rejected"
+RUN luks2-keyslot-invalid-area.img "F" "Invalid keyslot area object not rejected"
+RUN luks2-keyslot-invalid-area-size.img "F" "Invalid keyslot area size that can overflow not rejected"
+RUN luks2-keyslot-invalid-objects.img "F" "Invalid keyslot objects not rejected"
+RUN luks2-keyslot-invalid-af.img "F" "Invalid keyslot objects types not rejected"
+
+remove_mapping
+
+test $FAILS -eq 0 || fail "($FAILS wrong result(s) in total)"
diff --git a/tests/luks2_header_requirements.tar.xz b/tests/luks2_header_requirements.tar.xz
new file mode 100644
index 0000000..b198fd5
--- /dev/null
+++ b/tests/luks2_header_requirements.tar.xz
Binary files differ
diff --git a/tests/luks2_keyslot_unassigned.img.xz b/tests/luks2_keyslot_unassigned.img.xz
new file mode 100644
index 0000000..726cdd7
--- /dev/null
+++ b/tests/luks2_keyslot_unassigned.img.xz
Binary files differ
diff --git a/tests/luks2_mda_images.tar.xz b/tests/luks2_mda_images.tar.xz
new file mode 100644
index 0000000..a27f5c7
--- /dev/null
+++ b/tests/luks2_mda_images.tar.xz
Binary files differ
diff --git a/tests/luks2_valid_hdr.img.xz b/tests/luks2_valid_hdr.img.xz
new file mode 100644
index 0000000..4e03a20
--- /dev/null
+++ b/tests/luks2_valid_hdr.img.xz
Binary files differ
diff --git a/tests/mode-test b/tests/mode-test
new file mode 100755
index 0000000..82171fb
--- /dev/null
+++ b/tests/mode-test
@@ -0,0 +1,187 @@
+#!/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
+FAST_PBKDF2="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+# 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)
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+dmremove() { # device
+ udevadm settle >/dev/null 2>&1
+ dmsetup remove --retry $1 >/dev/null 2>&1
+}
+
+cleanup() {
+ [ -b /dev/mapper/"$DEV_NAME"_tstdev ] && dmremove "$DEV_NAME"_tstdev
+ [ -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
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+
+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 $FAST_PBKDF2 -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 $FAST_PBKDF2 -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."
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+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..6e3c78c
--- /dev/null
+++ b/tests/password-hash-test
@@ -0,0 +1,213 @@
+#!/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"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+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
+}
+
+skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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} "$@"
+}
+
+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" != "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
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+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..453831d
--- /dev/null
+++ b/tests/reencryption-compat-test
@@ -0,0 +1,489 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+REENC_BIN=$CRYPTSETUP
+REENC="$REENC_BIN reencrypt"
+FAST_PBKDF="--pbkdf-force-iterations 1000 --pbkdf pbkdf2"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=reenc9768
+DEV_NAME2=reenc1273
+IMG=reenc-data
+IMG_HDR=$IMG.hdr
+HEADER_LUKS2_PV=blkid-luks2-pv.img
+ORIG_IMG=reenc-data-orig
+DEV_LINK="reenc-test-link"
+KEY1=key1
+PWD1="93R4P4pIqAH8"
+PWD2="1cND4319812f"
+PWD3="1-9Qu5Ejfnqv"
+
+MNT_DIR=./mnt_luks
+START_DIR=$(pwd)
+FIPS_MODE=$(cat /proc/sys/crypto/fips_enabled 2>/dev/null)
+
+function fips_mode()
+{
+ [ -n "$FIPS_MODE" ] && [ "$FIPS_MODE" -gt 0 ]
+}
+
+function del_scsi_device()
+{
+ rmmod scsi_debug >/dev/null 2>&1
+ 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 $HEADER_LUKS2_PV $DEV_LINK >/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 valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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 add_scsi_device() {
+ del_scsi_device
+ if [ -d /sys/module/scsi_debug ] ; then
+ echo "Cannot use scsi_debug module (in use or compiled-in), test skipped."
+ exit 77
+ fi
+ modprobe scsi_debug $@ delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ echo "This kernel seems to not support proper scsi_debug module, test skipped."
+ exit 77
+ fi
+
+ sleep 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
+}
+
+function check_blkid() {
+ xz -dkf $HEADER_LUKS2_PV.xz
+ if ! $($CRYPTSETUP --version | grep -q "BLKID"); then
+ HAVE_BLKID=0
+ elif $(blkid -p -n crypto_LUKS $HEADER_LUKS2_PV >/dev/null 2>&1); then
+ HAVE_BLKID=1
+ else
+ HAVE_BLKID=0
+ fi
+}
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+[ ! -x "$REENC_BIN" ] && skip "Cannot find $REENC_BIN, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+command -v wipefs >/dev/null || 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 || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 256 $FAST_PBKDF || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 256 -c aes-xts-plain64 -h sha256 $FAST_PBKDF || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --use-directio $FAST_PBKDF || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q --volume-key-file /dev/urandom $FAST_PBKDF || fail
+check_hash $PWD1 $HASH1
+echo $PWD1 | $REENC $LOOPDEV1 -q -s 512 --volume-key-file /dev/urandom $FAST_PBKDF || fail
+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"
+# Encrypt without size reduction must not allow header device same as data device
+wipe_dev $LOOPDEV1
+echo $PWD1 | $REENC $LOOPDEV1 --type luks1 --new --header $LOOPDEV1 -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $LOOPDEV1 2>/dev/null && fail
+ln -s $LOOPDEV1 $DEV_LINK || fail
+echo $PWD1 | $REENC $LOOPDEV1 --type luks1 --new --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $LOOPDEV1 2>/dev/null && fail
+rm -f $DEV_LINK || fail
+echo $PWD1 | $REENC $IMG --type luks1 --new --header $IMG -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail
+ln -s $IMG $DEV_LINK || fail
+echo $PWD1 | $REENC $IMG --type luks1 --new --header $DEV_LINK -q $FAST_PBKDF_ARGON 2>/dev/null && fail
+$CRYPTSETUP isLUKS $IMG 2>/dev/null && fail
+
+if [ ! fips_mode ]; then
+# well, movin' zeroes :-)
+OFFSET=2048
+SIZE=$(blockdev --getsz $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
+fi
+
+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 sha512 $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 || fail
+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 || fail
+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 || fail
+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
+
+if [ ! fips_mode ]; then
+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 || fail
+check_hash $PWD1 $HASH4 $IMG_HDR
+echo $PWD1 | $REENC $LOOPDEV1 -q --header $IMG_HDR --decrypt || fail
+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
+
+echo "[12] Prevent nested encryption"
+prepare 8192
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 $FAST_PBKDF $LOOPDEV1 || fail
+
+#data device is already LUKS device (prevent nested encryption)
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 --reduce-device-size 1024S 2>/dev/null && fail
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 --header $IMG_HDR 2>/dev/null && fail
+test -f $IMG_HDR && fail
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks2 --reduce-device-size 2048S 2>/dev/null && fail
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks2 --header $IMG_HDR 2>/dev/null && fail
+test -f $IMG_HDR && fail
+
+wipe_dev $LOOPDEV1
+echo $PWD1 | $CRYPTSETUP -q luksFormat --type luks1 --header $IMG_HDR $FAST_PBKDF $LOOPDEV1 || fail
+
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 --header $IMG_HDR 2>/dev/null && fail
+echo $PWD1 | $REENC $LOOPDEV1 -q $FAST_PBKDF --new --type luks2 --header $IMG_HDR 2>/dev/null && fail
+
+check_blkid
+if [ "$HAVE_BLKID" -gt 0 ]; then
+ echo "[13] Prevent nested encryption of broken LUKS device"
+ rm -f $IMG_HDR
+ wipe_dev $LOOPDEV1
+ xz -dkf $HEADER_LUKS2_PV.xz
+ # broken header
+ echo $PWD1 | $REENC --header $HEADER_LUKS2_PV $LOOPDEV1 -q $FAST_PBKDF --new --type luks1 2>/dev/null && fail
+ $CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
+ # broken device
+ echo $PWD1 | $REENC $HEADER_LUKS2_PV -q $FAST_PBKDF --new --type luks1 --reduce-device-size 1024S 2>/dev/null && fail
+ $CRYPTSETUP isLuks $HEADER_LUKS2_PV && fail
+ # broken data device only
+ echo $PWD1 | $REENC --header $IMG_HDR $HEADER_LUKS2_PV -q $FAST_PBKDF --new --type luks1 2>/dev/null && fail
+ test -f $IMG_HDR && fail
+fi
+fi # if [ ! fips_mode ]
+
+remove_mapping
+exit 0
diff --git a/tests/run-all-symbols b/tests/run-all-symbols
new file mode 100755
index 0000000..775d5bb
--- /dev/null
+++ b/tests/run-all-symbols
@@ -0,0 +1,21 @@
+#!/bin/bash
+
+DIR=../.libs
+FILE=$DIR/libcryptsetup.so
+
+function fail()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ exit 77
+}
+
+test -d $DIR || fail "Directory $DIR is missing."
+test -f $FILE || skip "WARNING: Shared $FILE is missing, test skipped."
+
+./all-symbols-test $FILE $@
diff --git a/tests/ssh-test-plugin b/tests/ssh-test-plugin
new file mode 100755
index 0000000..5b3966e
--- /dev/null
+++ b/tests/ssh-test-plugin
@@ -0,0 +1,204 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && {
+ TOKEN_PATH="./fake_token_path.so"
+ [ ! -f $TOKEN_PATH ] && { echo "Please compile $TOKEN_PATH."; exit 77; }
+ export LD_PRELOAD=$TOKEN_PATH
+ CRYPTSETUP_PATH=".."
+}
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+CRYPTSETUP_SSH=$CRYPTSETUP_PATH/cryptsetup-ssh
+IMG="ssh_test.img"
+MAP="sshtest"
+USER="sshtest"
+PASSWD="sshtest1"
+PASSWD2="sshtest2"
+SSH_OPTIONS="-o StrictHostKeyChecking=no"
+
+SSH_SERVER="localhost"
+SSH_PATH="/home/$USER/keyfile"
+SSH_KEY_PATH="$HOME/sshtest-key"
+
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_SSH_VALGRIND=../.libs/cryptsetup-ssh
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+[ -z "$srcdir" ] && srcdir="."
+
+function remove_mapping()
+{
+ [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP
+ rm -f $IMG >/dev/null 2>&1
+}
+
+function remove_user()
+{
+ id -u $USER >/dev/null 2>&1 && userdel -r -f $USER >/dev/null 2>&1
+ rm -f $SSH_KEY_PATH "$SSH_KEY_PATH.pub" >/dev/null 2>&1
+}
+
+function create_user()
+{
+ id -u $USER >/dev/null 2>&1
+ [ $? -eq 0 ] && skip "User account $USER exists, aborting."
+ [ -f $SSH_KEY_PATH ] && skip "SSH key $SSH_KEY_PATH already exists, aborting."
+
+ useradd -m $USER -p $(openssl passwd $PASSWD) || skip "Failed to add user for SSH plugin test."
+
+ ssh-keygen -f $SSH_KEY_PATH -q -N "" >/dev/null 2>&1
+ [ $? -ne 0 ] && remove_user && skip "Failed to create SSH key."
+}
+
+function ssh_check()
+{
+ # try to use netcat to check port 22
+ nc -zv $SSH_SERVER 22 >/dev/null 2>&1 || skip "SSH server does not seem to be running, skipping."
+}
+
+function bin_check()
+{
+ command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
+}
+
+function ssh_setup()
+{
+ # copy the ssh key
+ [ -d "/home/$USER/.ssh" ] || mkdir /home/$USER/.ssh
+ touch /home/$USER/.ssh/authorized_keys
+
+ cat $SSH_KEY_PATH.pub >> /home/$USER/.ssh/authorized_keys
+ [ $? -ne 0 ] && remove_user && fail "Failed to copy SSH key."
+
+ # make sure /home/sshtest/.ssh and /home/sshtest/.ssh/authorized_keys have correct permissions
+ chown -R $USER:$USER /home/$USER/.ssh
+ chmod 700 /home/$USER/.ssh
+ chmod 644 /home/$USER/.ssh/authorized_keys
+
+ # try to ssh and also create keyfile
+ ssh -i $SSH_KEY_PATH $SSH_OPTIONS -o BatchMode=yes -n $USER@$SSH_SERVER "echo -n $PASSWD > $SSH_PATH" >/dev/null 2>&1
+ [ $? -ne 0 ] && remove_user && fail "Failed to connect using SSH."
+}
+
+function fail()
+{
+ echo "[FAILED]"
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED backtrace:"
+ while caller $frame; do ((frame++)); done
+ remove_mapping
+ remove_user
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ remove_mapping
+ exit 77
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || fail "Cannot find valgrind."
+ [ ! -f $CRYPTSETUP_VALGRIND ] && fail "Unable to get location of cryptsetup executable."
+ [ ! -f $CRYPTSETUP_SSH_VALGRIND ] && fail "Unable to get location of cryptsetup-ssh 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 valgrind_run_ssh()
+{
+ INFOSTRING="$(basename ${BASH_SOURCE[1]})-line-${BASH_LINENO[0]}" ./valg.sh ${CRYPTSETUP_SSH_VALGRIND} "$@"
+}
+
+format()
+{
+ dd if=/dev/zero of=$IMG bs=1M count=32 >/dev/null 2>&1
+
+ echo $PASSWD | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_OPT $IMG --force-password -q
+ [ $? -ne 0 ] && fail "Format failed."
+
+ echo -e "$PASSWD\n$PASSWD2" | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $IMG -q
+ [ $? -ne 0 ] && fail "Add key failed."
+}
+
+check_dump()
+{
+ dump=$1
+ keyslot=$2
+
+ token=$(echo "$dump" | grep Tokens -A 1 | tail -1 | cut -d: -f2 | tr -d "\t\n ")
+ [ "$token" = "ssh" ] || fail " token check from dump failed."
+
+ server=$(echo "$dump" | grep ssh_server | cut -d: -f2 | tr -d "\t\n ")
+ [ "$server" = $SSH_SERVER ] || fail " server check from dump failed."
+
+ user=$(echo "$dump" | grep ssh_user | cut -d: -f2 | tr -d "\t\n ")
+ [ "$user" = "$USER" ] || fail " user check from dump failed."
+
+ path=$(echo "$dump" | grep ssh_path | cut -d: -f2 | tr -d "\t\n ")
+ [ "$path" = "$SSH_PATH" ] || fail " path check from dump failed."
+
+ key_path=$(echo "$dump" | grep ssh_key_path | cut -d: -f2 | tr -d "\t\n ")
+ [ "$key_path" = "$SSH_KEY_PATH" ] || fail " key_path check from dump failed."
+
+ keyslot_dump=$(echo "$dump" | grep Keyslot: | cut -d: -f2 | tr -d "\t\n ")
+ [ "$keyslot_dump" = "$keyslot" ] || fail " keyslot check from dump failed."
+}
+
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run && CRYPTSETUP_SSH=valgrind_run_ssh
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+
+# Prevent running dangerous useradd operation by default
+[ -z "$RUN_SSH_PLUGIN_TEST" ] && skip "WARNING: Variable RUN_SSH_PLUGIN_TEST must be defined, test skipped."
+
+bin_check nc
+bin_check useradd
+bin_check ssh
+bin_check ssh-keygen
+bin_check sshpass
+bin_check openssl
+
+format
+
+echo -n "Adding SSH token: "
+
+ssh_check
+create_user
+ssh_setup
+
+$CRYPTSETUP_SSH add $IMG --ssh-server $SSH_SERVER --ssh-user $USER --ssh-path $SSH_PATH --ssh-keypath $SSH_KEY_PATH
+[ $? -ne 0 ] && fail "Failed to add SSH token to $IMG"
+
+out=$($CRYPTSETUP luksDump $IMG)
+check_dump "$out" 0
+echo "[OK]"
+
+echo -n "Activating using SSH token: "
+
+$CRYPTSETUP luksOpen --token-only --disable-external-tokens -r $IMG $MAP && fail "Tokens should be disabled"
+$CRYPTSETUP luksOpen -r $IMG $MAP -q >/dev/null 2>&1 <&-
+[ $? -ne 0 ] && fail "Failed to open $IMG using SSH token"
+echo "[OK]"
+
+# Remove the newly added token and test adding with --key-slot
+$CRYPTSETUP token remove --token-id 0 $IMG || fail "Failed to remove token"
+
+echo -n "Adding SSH token with --key-slot: "
+
+$CRYPTSETUP_SSH add $IMG --ssh-server $SSH_SERVER --ssh-user $USER --ssh-path $SSH_PATH --ssh-keypath $SSH_KEY_PATH --key-slot 1
+[ $? -ne 0 ] && fail "Failed to add SSH token to $IMG"
+
+out=$($CRYPTSETUP luksDump $IMG)
+check_dump "$out" 1
+echo "[OK]"
+
+remove_mapping
+remove_user
diff --git a/tests/systemd-test-plugin b/tests/systemd-test-plugin
new file mode 100755
index 0000000..5f37324
--- /dev/null
+++ b/tests/systemd-test-plugin
@@ -0,0 +1,150 @@
+#!/bin/bash
+
+CC="cc"
+
+PASSWD="tpm2_test"
+PASSWD2="tpm2_test2"
+FAST_PBKDF_OPT="--pbkdf pbkdf2 --pbkdf-force-iterations 1000"
+IMG=systemd_token_test.img
+MAP="systemd_tpm2_test"
+
+function bin_check()
+{
+ command -v $1 >/dev/null || skip "WARNING: test require $1 binary, test skipped."
+}
+
+function cleanup() {
+ [ -S $SWTPM_STATE_DIR/ctrl.sock ] && {
+ # shutdown TPM via control socket
+ swtpm_ioctl -s --unix $SWTPM_STATE_DIR/ctrl.sock
+ sleep 1
+ }
+
+ # if graceful shutdown was successful, pidfile should be deleted
+ # if it is still present, we forcefully kill the process
+ [ -f "$SWTPM_PIDFILE" ] && {
+ kill -9 $(cat $SWTPM_PIDFILE) >/dev/null 2>&1
+ }
+
+ [ -b /dev/mapper/$MAP ] && dmsetup remove --retry $MAP
+
+ rm -f $SWTPM_PIDFILE >/dev/null 2>&1
+ rm -rf $SWTPM_STATE_DIR >/dev/null 2>&1
+ rm -f $IMG >/dev/null 2>&1
+}
+
+function fail()
+{
+ echo "[FAILED]"
+ [ -n "$1" ] && echo "$1"
+ echo "FAILED backtrace:"
+ while caller $frame; do ((frame++)); done
+ cleanup
+ exit 2
+}
+
+function skip()
+{
+ [ -n "$1" ] && echo "$1"
+ cleanup
+ exit 77
+}
+
+# Prevent downloading and compiling systemd by default
+[ -z "$RUN_SYSTEMD_PLUGIN_TEST" ] && skip "WARNING: Variable RUN_SYSTEMD_PLUGIN_TEST must be defined, test skipped."
+
+[ $(id -u) != 0 ] && skip "WARNING: You must be root to run this test, test skipped."
+bin_check swtpm
+bin_check swtpm_ioctl
+
+CRYPTENROLL_LD_PRELOAD=""
+
+# if CRYPTSETUP_PATH is defined, we run against installed binaries,
+# otherwise we compile systemd tokens from source
+[ -z "$CRYPTSETUP_PATH" ] && {
+ bin_check git
+ bin_check meson
+ bin_check ninja
+ bin_check pkgconf
+
+ TOKEN_PATH=fake_token_path.so
+ [ -f $TOKEN_PATH ] || skip "Please compile $TOKEN_PATH."
+ INSTALL_PATH=$(pwd)/external-tokens/install
+ make -C .. install DESTDIR=$INSTALL_PATH
+ PC_FILE="$(find $INSTALL_PATH -name 'libcryptsetup.pc')"
+ sed -i "s/^prefix=/prefix=${INSTALL_PATH//\//\\\/}/g" "$PC_FILE"
+ export PKG_CONFIG_PATH=$(dirname $PC_FILE)
+
+ # systemd build system misses libcryptsetup.h if it is installed in non-default path
+ export CFLAGS="${CFLAGS:-} $(pkgconf --cflags libcryptsetup)"
+
+ SYSTEMD_PATH=$(pwd)/external-tokens/systemd
+ CRYPTSETUP_PATH=$(pwd)/..
+ SYSTEMD_CRYPTENROLL=$SYSTEMD_PATH/build/systemd-cryptenroll
+
+ mkdir -p $SYSTEMD_PATH
+ [ "$(ls -A $SYSTEMD_PATH)" ] || git clone --depth=1 https://github.com/systemd/systemd.git $SYSTEMD_PATH
+ cd $SYSTEMD_PATH
+ meson -D tpm2=true -D libcryptsetup=true -D libcryptsetup-plugins=true build/ || skip "Failed to configure systemd via meson, some dependencies are probably missing."
+ ninja -C build/ systemd-cryptenroll libcryptsetup-token-systemd-tpm2.so || skip "Failed to build systemd."
+
+ cd $CRYPTSETUP_PATH/tests
+ cp $SYSTEMD_PATH/build/libcryptsetup-token-*.so ../.libs/
+ cp $SYSTEMD_PATH/build/src/shared/*.so ../.libs/
+
+ export LD_PRELOAD="${LD_PRELOAD-}:$CRYPTSETUP_PATH/tests/$TOKEN_PATH"
+ CRYPTENROLL_LD_PRELOAD="$CRYPTSETUP_PATH/.libs/libcryptsetup.so"
+}
+CRYPTSETUP=$CRYPTSETUP_PATH/cryptsetup
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+
+[ -z "$SYSTEMD_CRYPTENROLL" ] && {
+ bin_check systemd-cryptenroll
+ SYSTEMD_CRYPTENROLL="systemd-cryptenroll"
+}
+
+[ -z "$TPM_PATH" ] && {
+ echo "Setting up virtual TPM using swtpm..."
+ SWTPM_PIDFILE=$(mktemp /tmp/systemd_swtpm_pid.XXXXXX)
+ SWTPM_STATE_DIR=$(mktemp -d /tmp/systemd_swtpm_state.XXXXXX)
+ modprobe tpm_vtpm_proxy || skip "Failed to load tpm_vtpm_proxy kernel module, required for emulated TPM."
+ SWTPM_LOG=$(swtpm chardev --vtpm-proxy --tpm2 --tpmstate dir=$SWTPM_STATE_DIR -d --pid file=$SWTPM_PIDFILE --ctrl type=unixio,path=$SWTPM_STATE_DIR/ctrl.sock)
+ TPM_PATH=$(echo $SWTPM_LOG | grep -Eo '/dev/tpm([0-9])+' | sed 's/tpm/tpmrm/')
+ [ -z "$TPM_PATH" ] && skip "No TPM_PATH set and swtpm failed, test skipped."
+ sleep 1
+ echo "Virtual TPM set up at $TPM_PATH"
+}
+
+FAKE_TPM_PATH="$(pwd)/fake_systemd_tpm_path.so"
+[ -f $FAKE_TPM_PATH ] || skip "Please compile $FAKE_TPM_PATH."
+export LD_PRELOAD="$LD_PRELOAD:$FAKE_TPM_PATH"
+
+export TPM_PATH=$TPM_PATH
+echo "TPM path is $TPM_PATH"
+
+dd if=/dev/zero of=$IMG bs=1M count=32 >/dev/null 2>&1
+echo $PASSWD | $CRYPTSETUP luksFormat --type luks2 $FAST_PBKDF_OPT $IMG --force-password -q
+
+echo "Enrolling the device to TPM 2 using systemd-cryptenroll.."
+LD_PRELOAD="$LD_PRELOAD:$CRYPTENROLL_LD_PRELOAD" PASSWORD="$PASSWD" $SYSTEMD_CRYPTENROLL $IMG --tpm2-device=$TPM_PATH >/dev/null 2>&1
+
+$CRYPTSETUP luksDump $IMG | grep -q "tpm2-blob" || fail "Failed to dump $IMG using systemd_tpm2 token (no tpm2-blob in output)."
+echo "Activating the device via TPM2 external token.."
+$CRYPTSETUP open --token-only $IMG $MAP >/dev/null 2>&1 || fail "Failed to open $IMG using systemd_tpm2 token."
+$CRYPTSETUP close $MAP >/dev/null 2>&1 || fail "Failed to close $MAP."
+
+echo "Adding passphrase via TPM2 token.."
+echo $PASSWD2 | $CRYPTSETUP luksAddKey $FAST_PBKDF_OPT $IMG --force-password -q --token-only >/dev/null 2>&1 || fail "Failed to add passphrase by tpm2 token."
+echo $PASSWD2 | $CRYPTSETUP open $IMG --test-passphrase --disable-external-tokens >/dev/null 2>&1 || fail "Failed to test passphrase added by tpm2 token."
+
+echo "Exporting and removing TPM2 token.."
+EXPORTED_TOKEN=$($CRYPTSETUP token export $IMG --token-id 0)
+$CRYPTSETUP token remove $IMG --token-id 0
+$CRYPTSETUP open $IMG --test-passphrase --token-only >/dev/null 2>&1 && fail "Activating without passphrase should fail after TPM2 token removal."
+
+echo "Re-importing TPM2 token.."
+echo $EXPORTED_TOKEN | $CRYPTSETUP token import $IMG --token-id 0 || fail "Failed to re-import deleted token."
+$CRYPTSETUP open $IMG --test-passphrase --token-only >/dev/null 2>&1 || fail "Failed to activate after re-importing deleted token."
+
+cleanup
+exit 0
diff --git a/tests/tcrypt-compat-test b/tests/tcrypt-compat-test
new file mode 100755
index 0000000..c0fc50a
--- /dev/null
+++ b/tests/tcrypt-compat-test
@@ -0,0 +1,223 @@
+#!/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
+
+CRYPTSETUP_VALGRIND=../.libs/cryptsetup
+CRYPTSETUP_LIB_VALGRIND=../.libs
+
+[ -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
+ rm -rf $TST_DIR
+}
+
+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"
+ remove_mapping
+ 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 get_HASH_CIPHER() # filename
+{
+ # speed up the test by limiting options for hash and (first) cipher
+ HASH=$(echo $file | cut -d'-' -f3)
+ CIPHER=$(echo $file | cut -d'-' -f5)
+}
+
+function test_required()
+{
+ command -v blkid >/dev/null || skip "blkid tool required, test skipped."
+
+ 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, test skipped."
+}
+
+function valgrind_setup()
+{
+ command -v valgrind >/dev/null || 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
+[ ! -x "$CRYPTSETUP" ] && skip "Cannot find $CRYPTSETUP, test skipped."
+[ ! -d $TST_DIR ] && tar xJf $srcdir/tcrypt-images.tar.xz --no-same-owner
+
+[ -n "$VALG" ] && valgrind_setup && CRYPTSETUP=valgrind_run
+
+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"
+ get_HASH_CIPHER $file
+ echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null || fail
+ if [[ $file =~ .*-sha512-xts-aes$ ]] ; then
+ echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c aes $file >/dev/null || fail
+ echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h xxxx $file 2>/dev/null && fail
+ echo $PASSWORD | $CRYPTSETUP tcryptDump $SYS_OPT $PIM_OPT -h sha512 -c xxx $file 2>/dev/null && fail
+ fi
+ echo " [OK]"
+done
+
+echo "HEADER CHECK (TCRYPT only)"
+for file in $(ls $TST_DIR/vc_* $TST_DIR/vcpim_*) ; do
+ echo -n " $file"
+ PIM_OPT=""
+ [[ $file =~ vcpim.* ]] && PIM_OPT="--veracrypt-pim $PIM"
+ get_HASH_CIPHER $file
+ echo $PASSWORD | $CRYPTSETUP tcryptDump --disable-veracrypt $PIM_OPT -h $HASH -c $CIPHER $file >/dev/null 2>&1 && fail
+ echo " [OK]"
+done
+
+echo "HEADER CHECK (HIDDEN)"
+for file in $(ls $TST_DIR/[tv]c_*-hidden) ; do
+ echo -n " $file (hidden)"
+ get_HASH_CIPHER $file
+ echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptDump --tcrypt-hidden -h $HASH -c $CIPHER $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
+ get_HASH_CIPHER $file
+ echo $PWD | $CRYPTSETUP tcryptDump -d $TST_DIR/keyfile1 -d $TST_DIR/keyfile2 -h $HASH -c $CIPHER $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."
+ remove_mapping
+ 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"
+ get_HASH_CIPHER $file
+ out=$(echo $PASSWORD | $CRYPTSETUP tcryptOpen $SYS_OPT $PIM_OPT -r -h $HASH -c $CIPHER $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=$(blkid -p -o value -s 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"
+ get_HASH_CIPHER $file
+ out=$(echo $PASSWORD_HIDDEN | $CRYPTSETUP tcryptOpen -r -h $HASH -c $CIPHER $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=$(blkid -p -o value -s UUID /dev/mapper/$MAP)
+ $CRYPTSETUP remove $MAP || fail
+ [ "$UUID" != "CAFE-BABE" ] && fail "UUID check failed."
+ echo " [OK]"
+done
+
+remove_mapping
+exit 0
diff --git a/tests/tcrypt-images.tar.xz b/tests/tcrypt-images.tar.xz
new file mode 100644
index 0000000..1841870
--- /dev/null
+++ b/tests/tcrypt-images.tar.xz
Binary files differ
diff --git a/tests/test_utils.c b/tests/test_utils.c
new file mode 100644
index 0000000..97c62a0
--- /dev/null
+++ b/tests/test_utils.c
@@ -0,0 +1,950 @@
+/*
+ * cryptsetup library API test utilities
+ *
+ * Copyright (C) 2009-2023 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2009-2023 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 <assert.h>
+#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"
+
+#ifndef LOOP_CONFIGURE
+#define LOOP_CONFIGURE 0x4C0A
+struct loop_config {
+ __u32 fd;
+ __u32 block_size;
+ struct loop_info64 info;
+ __u64 __reserved[8];
+};
+#endif
+
+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 t_set_readahead(const char *device, unsigned value)
+{
+ int devfd, r = 0;
+
+ devfd = open(device, O_RDONLY);
+ if(devfd == -1)
+ return -EINVAL;
+
+ if (ioctl(devfd, BLKRASET, value) < 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;
+ }
+ r = snprintf(cmd, sizeof(cmd),
+ "dmsetup create %s --table \"0 %" PRIu64 " linear %s %" PRIu64 "\"",
+ dm_name, size, THE_LOOP_DEV, t_dev_offset);
+ if (r < 0 || (size_t)r >= sizeof(cmd))
+ return -3;
+
+ if (!(r = _system(cmd, 1)))
+ t_dev_offset += size;
+ return r;
+}
+
+__attribute__((format(printf, 3, 4)))
+static int _snprintf(char **r_ptr, size_t *r_remains, const char *format, ...)
+{
+ int len, r = 0;
+ va_list argp;
+
+ assert(r_remains);
+ assert(r_ptr);
+
+ va_start(argp, format);
+
+ len = vsnprintf(*r_ptr, *r_remains, format, argp);
+ if (len < 0 || (size_t)len >= *r_remains) {
+ r = -EINVAL;
+ } else {
+ *r_ptr += len;
+ *r_remains -= len;
+ }
+
+ va_end(argp);
+
+ return r;
+}
+
+int dmdevice_error_io(const char *dm_name,
+ const char *dm_device,
+ const char *error_device,
+ uint64_t data_offset,
+ uint64_t offset,
+ uint64_t length,
+ error_io_info ei)
+{
+ char str[256], cmd[384];
+ int r;
+ uint64_t dev_size;
+ size_t remains;
+ char *ptr;
+
+ if (t_device_size(dm_device, &dev_size) < 0 || !length)
+ return -1;
+
+ dev_size >>= TST_SECTOR_SHIFT;
+
+ if (dev_size <= offset)
+ return -1;
+
+ if (ei == ERR_REMOVE) {
+ r = snprintf(cmd, sizeof(cmd),
+ "dmsetup load %s --table \"0 %" PRIu64 " linear %s %" PRIu64 "\"",
+ dm_name, dev_size, THE_LOOP_DEV, data_offset);
+ if (r < 0 || (size_t)r >= sizeof(str))
+ return -3;
+
+ if ((r = _system(cmd, 1)))
+ return r;
+
+ r = snprintf(cmd, sizeof(cmd), "dmsetup resume %s", dm_name);
+ if (r < 0 || (size_t)r >= sizeof(cmd))
+ return -3;
+
+ return _system(cmd, 1);
+ }
+
+ if ((dev_size - offset) < length) {
+ printf("Not enough space on target device\n.");
+ return -2;
+ }
+
+ remains = sizeof(str);
+ ptr = str;
+
+ if (offset) {
+ r = _snprintf(&ptr, &remains,
+ "0 %" PRIu64 " linear %s %" PRIu64 "\n",
+ offset, THE_LOOP_DEV, data_offset);
+ if (r < 0)
+ return r;
+ }
+ r = _snprintf(&ptr, &remains, "%" PRIu64 " %" PRIu64 " delay ",
+ offset, length);
+ if (r < 0)
+ return r;
+
+ if (ei == ERR_RW || ei == ERR_RD) {
+ r = _snprintf(&ptr, &remains, "%s 0 0",
+ error_device);
+ if (r < 0)
+ return r;
+ if (ei == ERR_RD) {
+ r = _snprintf(&ptr, &remains, " %s %" PRIu64 " 0",
+ THE_LOOP_DEV, data_offset + offset);
+ if (r < 0)
+ return r;
+ }
+ } else if (ei == ERR_WR) {
+ r = _snprintf(&ptr, &remains, "%s %" PRIu64 " 0 %s 0 0",
+ THE_LOOP_DEV, data_offset + offset, error_device);
+ if (r < 0)
+ return r;
+ }
+
+ if (dev_size > (offset + length)) {
+ r = _snprintf(&ptr, &remains,
+ "\n%" PRIu64 " %" PRIu64 " linear %s %" PRIu64,
+ offset + length, dev_size - offset - length, THE_LOOP_DEV,
+ data_offset + offset + length);
+ if (r < 0)
+ return r;
+ }
+
+ /*
+ * Hello darkness, my old friend...
+ *
+ * On few old distributions there's issue with
+ * processing multiline tables via dmsetup load --table.
+ * This workaround passes on all systems we run tests on.
+ */
+ r = snprintf(cmd, sizeof(cmd), "dmsetup load %s <<EOF\n%s\nEOF", dm_name, str);
+ if (r < 0 || (size_t)r >= sizeof(cmd))
+ return -3;
+
+ if ((r = _system(cmd, 1)))
+ return r;
+
+ r = snprintf(cmd, sizeof(cmd), "dmsetup resume %s", dm_name);
+ if (r < 0 || (size_t)r >= sizeof(cmd))
+ return -3;
+
+ if ((r = _system(cmd, 1)))
+ return r;
+
+ return t_set_readahead(dm_device, 0);
+}
+
+// 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;
+ 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;
+
+ dm_get_next_target(dmt, NULL, &start, &length, &target_type, &params);
+ if (!target_type || strcmp(target_type, "crypt") != 0)
+ goto out;
+
+ (void)strsep(&params, " "); /* rcipher */
+ key = strsep(&params, " ");
+
+ if (buffer_size <= strlen(key))
+ goto out;
+
+ strncpy(buffer, key, buffer_size);
+ r = 0;
+out:
+ if (dmt)
+ dm_task_destroy(dmt);
+
+ return r;
+}
+
+int prepare_keyfile(const char *name, const char *passphrase, int size)
+{
+ int fd, r;
+
+ fd = open(name, O_RDWR | O_CREAT | O_TRUNC, S_IRUSR|S_IWUSR);
+ if (fd != -1) {
+ r = write(fd, passphrase, size);
+ close(fd);
+ } else
+ r = 0;
+
+ return r == size ? 0 : 1;
+}
+
+// Decode key from its hex representation
+int crypt_decode_key(char *key, const char *hex, unsigned int size)
+{
+ char buffer[3];
+ char *endp;
+ unsigned int i;
+
+ buffer[2] = '\0';
+
+ for (i = 0; i < size; i++) {
+ buffer[0] = *hex++;
+ buffer[1] = *hex++;
+
+ key[i] = (unsigned char)strtoul(buffer, &endp, 16);
+
+ if (endp != &buffer[2])
+ return -1;
+ }
+
+ if (*hex != '\0')
+ return -1;
+
+ return 0;
+}
+
+void global_log_callback(int level, const char *msg, void *usrptr __attribute__((unused)))
+{
+ 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;
+
+ if (t_dm_satisfies_version(1, 17, 0, crypt_maj, crypt_min, crypt_patch)) {
+ t_dm_crypt_flags |= T_DM_SECTOR_SIZE_SUPPORTED;
+ t_dm_crypt_flags |= T_DM_CAPI_STRING_SUPPORTED;
+ }
+
+ if (t_dm_satisfies_version(1, 19, 0, crypt_maj, crypt_min, crypt_patch))
+ t_dm_crypt_flags |= T_DM_BITLK_EBOIV_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 20, 0, crypt_maj, crypt_min, crypt_patch))
+ t_dm_crypt_flags |= T_DM_BITLK_ELEPHANT_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 22, 0, crypt_maj, crypt_min, crypt_patch))
+ t_dm_crypt_flags |= T_DM_CRYPT_NO_WORKQUEUE_SUPPORTED;
+}
+
+static void t_dm_set_verity_compat(const char *dm_version __attribute__((unused)),
+ unsigned verity_maj,
+ unsigned verity_min,
+ unsigned verity_patch __attribute__((unused)))
+{
+ 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;
+ }
+
+ if (t_dm_satisfies_version(1, 5, 0, verity_maj, verity_min, verity_patch))
+ t_dm_crypt_flags |= T_DM_VERITY_SIGNATURE_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 7, 0, verity_maj, verity_min, verity_patch))
+ t_dm_crypt_flags |= T_DM_VERITY_PANIC_CORRUPTION_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 9, 0, verity_maj, verity_min, verity_patch))
+ t_dm_crypt_flags |= T_DM_VERITY_TASKLETS_SUPPORTED;
+}
+
+static void t_dm_set_integrity_compat(const char *dm_version __attribute__((unused)),
+ unsigned integrity_maj __attribute__((unused)),
+ unsigned integrity_min __attribute__((unused)),
+ unsigned integrity_patch __attribute__((unused)))
+{
+ if (integrity_maj > 0)
+ t_dm_crypt_flags |= T_DM_INTEGRITY_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 2, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_RECALC_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 3, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_BITMAP_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 4, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_FIX_PADDING_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 6, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_DISCARDS_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 7, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_FIX_HMAC_SUPPORTED;
+
+ if (t_dm_satisfies_version(1, 8, 0, integrity_maj, integrity_min, integrity_patch))
+ t_dm_crypt_flags |= T_DM_INTEGRITY_RESET_RECALC_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 = VOIDP_CAST(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;
+}
+
+int t_dm_integrity_resize_support(void)
+{
+ return t_dm_crypt_flags & T_DM_INTEGRITY_RESIZE_SUPPORTED;
+}
+
+int t_dm_integrity_recalculate_support(void)
+{
+ return t_dm_crypt_flags & T_DM_INTEGRITY_RECALC_SUPPORTED;
+}
+
+int t_dm_capi_string_supported(void)
+{
+ return t_dm_crypt_flags & T_DM_CAPI_STRING_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[64];
+ 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_config config = {0};
+ char *lo_file_name;
+ int loop_fd = -1, file_fd = -1, r = 1;
+ int fallback = 0;
+
+ *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;
+
+ config.fd = file_fd;
+
+ lo_file_name = (char*)config.info.lo_file_name;
+ lo_file_name[LO_NAME_SIZE-1] = '\0';
+ strncpy(lo_file_name, file, LO_NAME_SIZE-1);
+ config.info.lo_offset = offset;
+ if (autoclear)
+ config.info.lo_flags |= LO_FLAGS_AUTOCLEAR;
+
+ 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_CONFIGURE, &config) < 0) {
+ if (errno == EINVAL || errno == ENOTTY) {
+ free(*loop);
+ *loop = NULL;
+
+ close(loop_fd);
+ loop_fd = -1;
+
+ /* kernel doesn't support LOOP_CONFIGURE */
+ fallback = 1;
+ break;
+ }
+ if (errno != EBUSY)
+ goto out;
+ free(*loop);
+ *loop = NULL;
+
+ close(loop_fd);
+ loop_fd = -1;
+ }
+ }
+
+ if (fallback)
+ {
+ 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;
+ }
+ }
+
+ if (ioctl(loop_fd, LOOP_SET_STATUS64, &config.info) < 0) {
+ (void)ioctl(loop_fd, LOOP_CLR_FD, 0);
+ goto out;
+ }
+ }
+
+ /* Verify that autoclear is really set */
+ if (autoclear) {
+ memset(&config.info, 0, sizeof(config.info));
+ if (ioctl(loop_fd, LOOP_GET_STATUS64, &config.info) < 0 ||
+ !(config.info.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;
+}
+
+int t_get_devno(const char *name, dev_t *devno)
+{
+ char path[PATH_MAX];
+ int r;
+ struct stat st;
+
+ r = snprintf(path, sizeof(path), DMDIR "%s", name);
+ if (r < 0 || (size_t)r >= sizeof(path))
+ return 1;
+
+ if (stat(path, &st) || !S_ISBLK(st.st_mode))
+ return 1;
+
+ *devno = st.st_rdev;
+
+ return 0;
+}
+
+static int _read_uint64(const char *sysfs_path, uint64_t *value)
+{
+ char tmp[64] = {0};
+ int fd, r;
+
+ if ((fd = open(sysfs_path, O_RDONLY)) < 0)
+ return 0;
+ r = read(fd, tmp, sizeof(tmp));
+ close(fd);
+
+ if (r <= 0)
+ return 0;
+
+ if (sscanf(tmp, "%" PRIu64, value) != 1)
+ return 0;
+
+ return 1;
+}
+
+static int _sysfs_get_uint64(int major, int minor, uint64_t *value, const char *attr)
+{
+ char path[PATH_MAX];
+
+ if (snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/%s",
+ major, minor, attr) < 0)
+ return 0;
+
+ return _read_uint64(path, value);
+}
+
+int t_device_size_by_devno(dev_t devno, uint64_t *retval)
+{
+ if (!_sysfs_get_uint64(major(devno), minor(devno), retval, "size"))
+ return 1;
+
+ *retval *= 512;
+ return 0;
+}
diff --git a/tests/unit-utils-crypt.c b/tests/unit-utils-crypt.c
new file mode 100644
index 0000000..4ab3c96
--- /dev/null
+++ b/tests/unit-utils-crypt.c
@@ -0,0 +1,259 @@
+/*
+ * cryptsetup crypto name and hex conversion helper test vectors
+ *
+ * Copyright (C) 2022-2023 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 "utils_crypt.h"
+#include "libcryptsetup.h"
+
+#ifndef ARRAY_SIZE
+# define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
+#endif
+
+/*
+ * Cryptsetup/dm-crypt algorithm naming conversion test
+ */
+struct mode_test_vector {
+ const char *input;
+ const char *cipher;
+ const char *mode;
+ int keys;
+};
+static struct mode_test_vector mode_test_vectors[] = {
+ { "aes-xts-plain", "aes", "xts-plain", 1 },
+ { "aes-xts-plain64", "aes", "xts-plain64", 1 },
+ { "aes-cbc-plain", "aes", "cbc-plain", 1 },
+ { "aes-cbc-plain64", "aes", "cbc-plain64", 1 },
+ { "aes-cbc-essiv:sha256", "aes", "cbc-essiv:sha256", 1 },
+ { "aes", "aes", "cbc-plain", 1 },
+ { "twofish", "twofish", "cbc-plain", 1 },
+ { "cipher_null", "cipher_null", "ecb", 0 },
+ { "null", "cipher_null", "ecb", 0 },
+ { "xchacha12,aes-adiantum-plain64", "xchacha12,aes", "adiantum-plain64", 1 },
+ { "xchacha20,aes-adiantum-plain64", "xchacha20,aes", "adiantum-plain64", 1 },
+ { "aes:64-cbc-lmk", "aes:64", "cbc-lmk", 64 },
+ { "des3_ede-cbc-tcw", "des3_ede" ,"cbc-tcw", 1 },
+ { "aes-lrw-benbi", "aes","lrw-benbi", 1 },
+};
+
+static int test_parse_mode(void)
+{
+ char cipher[MAX_CIPHER_LEN], mode[MAX_CIPHER_LEN];
+ unsigned int i;
+ int keys;
+
+ printf("MODECONV:");
+ for (i = 0; i < ARRAY_SIZE(mode_test_vectors); i++) {
+ if (i && !(i % 8))
+ printf("\n");
+ keys = -1;
+ memset(cipher, 0, sizeof(cipher));
+ memset(mode, 0, sizeof(mode));
+ printf("[%s]", mode_test_vectors[i].input ?: "NULL");
+ if (crypt_parse_name_and_mode(mode_test_vectors[i].input, cipher, &keys, mode) < 0 ||
+ strcmp(mode_test_vectors[i].cipher, cipher) ||
+ strcmp(mode_test_vectors[i].mode, mode) ||
+ mode_test_vectors[i].keys != keys) {
+ printf("[FAILED (%s / %s / %i)]\n", cipher, mode, keys);
+ return EXIT_FAILURE;
+ }
+ }
+ printf("[OK]\n");
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Cryptsetup/dm-crypt/dm-integrity algorithm naming conversion test
+ */
+struct integrity_test_vector {
+ bool int_mode; /* non-null if it is supported as integrity mode for LUKS2 */
+ const char *input;
+ const char *integrity;
+ int key_size;
+};
+static struct integrity_test_vector integrity_test_vectors[] = {
+ { true, "aead", "aead", 0 },
+ { true, "poly1305", "poly1305", 0 },
+ { true, "none", "none", 0 },
+ { false, "crc32", "crc32", 0 },
+ { true, "hmac-sha1", "hmac(sha1)", 20 },
+ { true, "hmac-sha256", "hmac(sha256)", 32 },
+ { true, "hmac-sha512", "hmac(sha512)", 64 },
+ { true, "cmac-aes", "cmac(aes)", 16 },
+ { false, "blake2b-256", "blake2b-256", 0 },
+};
+
+static int test_parse_integrity_mode(void)
+{
+ char integrity[MAX_CIPHER_LEN];
+ unsigned int i;
+ int key_size;
+
+ printf("INTEGRITYCONV:");
+ for (i = 0; i < ARRAY_SIZE(integrity_test_vectors); i++) {
+ memset(integrity, 0, sizeof(integrity));
+ printf("[%s,%i]", integrity_test_vectors[i].input ?: "NULL", integrity_test_vectors[i].key_size);
+ if (crypt_parse_hash_integrity_mode(integrity_test_vectors[i].input, integrity) < 0 ||
+ strcmp(integrity_test_vectors[i].integrity, integrity)) {
+ printf("[FAILED (%s)]\n", integrity);
+ return EXIT_FAILURE;
+ }
+ key_size = -1;
+ memset(integrity, 0, sizeof(integrity));
+ if (integrity_test_vectors[i].int_mode &&
+ (crypt_parse_integrity_mode(integrity_test_vectors[i].input, integrity, &key_size) < 0 ||
+ strcmp(integrity_test_vectors[i].integrity, integrity) ||
+ integrity_test_vectors[i].key_size != key_size)) {
+ printf("[FAILED (%s / %i)]\n", integrity, key_size);
+ return EXIT_FAILURE;
+ }
+ }
+ printf("[OK]\n");
+
+ return EXIT_SUCCESS;
+}
+
+/*
+ * Cryptsetup null cipher bypass algorithm name
+ */
+struct null_test_vector {
+ const char *cipher;
+ bool ok;
+};
+static struct null_test_vector null_test_vectors[] = {
+ { "cipher_null-ecb", true },
+ { "cipher_null", true },
+ { "null", true },
+ { "cipher-null", false },
+ { "aes-ecb", false },
+ { NULL, false },
+};
+
+static int test_cipher_null(void)
+{
+ unsigned int i;
+
+ printf("NULLCONV:");
+ for (i = 0; i < ARRAY_SIZE(null_test_vectors); i++) {
+ printf("[%s]", null_test_vectors[i].cipher ?: "NULL");
+ if (crypt_is_cipher_null(null_test_vectors[i].cipher) !=
+ null_test_vectors[i].ok) {
+ printf("[FAILED]\n");
+ return EXIT_FAILURE;
+ }
+ }
+ printf("[OK]\n");
+
+ return EXIT_SUCCESS;
+}
+
+struct hex_test_vector {
+ const char *hex;
+ const char *bytes;
+ ssize_t bytes_size;
+ bool ok;
+};
+static struct hex_test_vector hex_test_vectors[] = {
+ { "0000000000000000", "\x00\x00\x00\x00\x00\x00\x00\x00", 8, true },
+ { "abcdef0123456789", "\xab\xcd\xef\x01\x23\x45\x67\x89", 8, true },
+ { "aBCDef0123456789", "\xab\xcd\xef\x01\x23\x45\x67\x89", 8, true },
+ { "ff", "\xff", 1, true },
+ { "f", NULL , 1, false },
+ { "a-cde", NULL, 2, false },
+ { "FAKE", NULL, 2, false },
+ { "\x01\x02\xff", NULL, 3, false },
+ { NULL, NULL, 1, false },
+ { "fff", NULL, 2, false },
+ { "fg", NULL, 1, false },
+};
+
+/*
+ * Hexa conversion test (also should be constant time)
+ */
+static int test_hex_conversion(void)
+{
+ char *bytes, *hex;
+ ssize_t len;
+ unsigned int i;
+
+ printf("HEXCONV:");
+ for (i = 0; i < ARRAY_SIZE(hex_test_vectors); i++) {
+ bytes = NULL;
+ hex = NULL;
+ if (hex_test_vectors[i].hex && *hex_test_vectors[i].hex >= '0')
+ printf("[%s]", hex_test_vectors[i].hex);
+ else
+ printf("[INV:%i]", i);
+ len = crypt_hex_to_bytes(hex_test_vectors[i].hex, &bytes, 1);
+ if ((hex_test_vectors[i].ok && len != hex_test_vectors[i].bytes_size) ||
+ (!hex_test_vectors[i].ok && len >= 0)) {
+ printf("[FAILED]\n");
+ crypt_safe_free(bytes);
+ return EXIT_FAILURE;
+ }
+ crypt_safe_free(bytes);
+ hex = crypt_bytes_to_hex(hex_test_vectors[i].bytes_size, hex_test_vectors[i].bytes);
+ if ((hex_test_vectors[i].ok && strcasecmp(hex, hex_test_vectors[i].hex)) ||
+ (!hex_test_vectors[i].ok && hex)) {
+ printf("[FAILED]\n");
+ crypt_safe_free(hex);
+ return EXIT_FAILURE;
+ }
+ crypt_safe_free(hex);
+ }
+ printf("[OK]\n");
+
+ return EXIT_SUCCESS;
+}
+
+static void __attribute__((noreturn)) exit_test(const char *msg, int r)
+{
+ if (msg)
+ printf("%s\n", msg);
+ exit(r);
+}
+
+int main(__attribute__ ((unused)) int argc, __attribute__ ((unused))char *argv[])
+{
+ setvbuf(stdout, NULL, _IONBF, 0);
+
+#ifndef NO_CRYPTSETUP_PATH
+ if (getenv("CRYPTSETUP_PATH")) {
+ printf("Cannot run this test with CRYPTSETUP_PATH set.\n");
+ exit(77);
+ }
+#endif
+ if (test_parse_mode())
+ exit_test("Parse mode test failed.", EXIT_FAILURE);
+
+ if (test_parse_integrity_mode())
+ exit_test("Parse integrity mode test failed.", EXIT_FAILURE);
+
+ if (test_cipher_null())
+ exit_test("CIPHER null test failed.", EXIT_FAILURE);
+
+ if (test_hex_conversion())
+ exit_test("HEX conversion test failed.", EXIT_FAILURE);
+
+ exit_test(NULL, EXIT_SUCCESS);
+}
diff --git a/tests/unit-utils-io.c b/tests/unit-utils-io.c
new file mode 100644
index 0000000..3bfc762
--- /dev/null
+++ b/tests/unit-utils-io.c
@@ -0,0 +1,352 @@
+/*
+ * simple unit test for utils_io.c (blockwise low level functions)
+ *
+ * Copyright (C) 2018-2023 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;
+
+#ifndef NO_CRYPTSETUP_PATH
+ if (getenv("CRYPTSETUP_PATH")) {
+ printf("Cannot run this test with CRYPTSETUP_PATH set.\n");
+ exit(77);
+ }
+#endif
+ 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/unit-wipe-test b/tests/unit-wipe-test
new file mode 100755
index 0000000..4d0a078
--- /dev/null
+++ b/tests/unit-wipe-test
@@ -0,0 +1,170 @@
+#!/bin/bash
+
+WIPE_UNIT=./unit-wipe
+FILE=./wipe_localfile
+FILE_RAND=./wipe_random_localfile
+MB_BYTES=$((1024*1024))
+DEVSIZEMB=8
+DEVSIZE=$((DEVSIZEMB*$MB_BYTES))
+
+HASH_EMPTY=2daeb1f36095b44b318410b3f4e8b5d989dcc7bb023d1426c492dab0a3053e74
+
+function cleanup() {
+ rm -f $FILE $FILE_RAND 2> /dev/null
+ sleep 1
+ rmmod scsi_debug >/dev/null 2>&1
+}
+
+function fail()
+{
+ if [ -n "$1" ] ; then echo "FAIL $1" ; else echo "FAIL" ; fi
+ echo "FAILED backtrace:"
+ while caller $frame; do ((frame++)); done
+ cleanup
+ exit 100
+}
+
+function skip()
+{
+ echo "TEST SKIPPED: $1"
+ cleanup
+ exit 77
+}
+
+function add_device()
+{
+ rmmod scsi_debug >/dev/null 2>&1
+ if [ -d /sys/module/scsi_debug ] ; then
+ skip "Cannot use scsi_debug module (in use or compiled-in)."
+ fi
+ modprobe scsi_debug dev_size_mb=$DEVSIZEMB num_tgts=1 delay=0 >/dev/null 2>&1
+ if [ $? -ne 0 ] ; then
+ skip "This kernel seems to not support proper scsi_debug module."
+ fi
+ grep -q scsi_debug /sys/block/*/device/model || 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_hash() # $1 dev, $2 hash
+{
+ local HASH=$(sha256sum $1 | cut -d' ' -f 1)
+ [ $HASH == "$2" ]
+}
+
+function init_hash_dd() # $1 dev, $dev orig
+{
+ dd if=/dev/urandom of=$2 bs=1M count=$DEVSIZEMB conv=notrunc 2> /dev/null
+ dd if=$2 of=$1 bs=1M conv=notrunc 2> /dev/null
+ HASH_0=$(sha256sum $1 | cut -d' ' -f 1)
+ # second MB wiped
+ dd if=/dev/zero of=$1 bs=1M seek=1 count=1 conv=notrunc 2> /dev/null
+ HASH_1=$(sha256sum $1 | cut -d' ' -f 1)
+ # 4,5,6 MB wiped
+ dd if=/dev/zero of=$1 bs=1M seek=4 count=3 conv=notrunc 2> /dev/null
+ HASH_2=$(sha256sum $1 | cut -d' ' -f 1)
+ dd if=$2 of=$1 bs=1M conv=notrunc 2> /dev/null
+}
+
+function add_file()
+{
+ dd if=/dev/zero of=$FILE bs=1M count=$DEVSIZEMB 2> /dev/null || fail
+ dd if=/dev/zero of=$FILE_RAND bs=1M count=$DEVSIZEMB 2> /dev/null || fail
+ check_hash $FILE $HASH_EMPTY || fail
+ check_hash $FILE_RAND $HASH_EMPTY || fail
+ dd if=$FILE of=/dev/null bs=4096 count=1 iflag=direct >/dev/null 2>&1 || FILE_NODIO=1
+}
+
+function test_wipe_full() # $1 dev, $2 block size, [$3 flags]
+{
+ # wipe random and back to zero
+ $WIPE_UNIT $1 random 0 $DEVSIZE $2 $3 || fail
+ check_hash $1 $HASH_EMPTY && fail "Failed random wipe"
+ $WIPE_UNIT $1 zero 0 $DEVSIZE $2 $3 || fail
+ check_hash $1 $HASH_EMPTY || fail "Failed zero wipe"
+}
+
+# wipe MB blocks, with zero, random and special and back to original
+function test_wipe_blocks() # $1 dev $2 block sizem [$3 flags]
+{
+ init_hash_dd $1 $FILE_RAND
+ check_hash $1 $HASH_0 || fail
+
+ $WIPE_UNIT $1 zero $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_1 || fail
+ $WIPE_UNIT $1 random $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_1 && fail
+ $WIPE_UNIT $1 special $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_1 && fail
+ $WIPE_UNIT $1 zero $((1*$MB_BYTES)) $((1*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_1 || fail
+
+ $WIPE_UNIT $1 zero $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_2 || fail
+ $WIPE_UNIT $1 random $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_2 && fail
+ $WIPE_UNIT $1 special $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_2 && fail
+ $WIPE_UNIT $1 zero $((4*$MB_BYTES)) $((3*$MB_BYTES)) $2 $3 || fail
+ check_hash $1 $HASH_2 || fail
+}
+
+test -x $WIPE_UNIT || skip "Run \"make `basename $WIPE_UNIT`\" first"
+
+cleanup
+add_file
+
+echo -n "[1] Wipe full file "
+for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do
+ if [ -n "$FILE_NODIO" ]; then
+ echo -n [$bs/DIO N/A]
+ else
+ echo -n [$bs/DIO]
+ test_wipe_full $FILE $bs
+ fi
+ echo -n [$bs]
+ test_wipe_full $FILE $bs no-dio
+done
+echo "[OK]"
+
+echo -n "[2] Wipe blocks in file "
+for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do
+ if [ -n "$FILE_NODIO" ]; then
+ echo -n [$bs/DIO N/A]
+ else
+ echo -n [$bs/DIO]
+ test_wipe_blocks $FILE $bs
+ fi
+ echo -n [$bs]
+ test_wipe_blocks $FILE $bs no-dio
+done
+echo "[OK]"
+
+[ $(id -u) -eq 0 ] || {
+ echo "WARNING: You must be root to run remaining tests."
+ cleanup
+ exit 0
+}
+
+add_device
+
+echo -n "[3] Wipe full block device "
+for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do
+ echo -n [$bs/DIO]
+ test_wipe_full $DEV $bs
+ echo -n [$bs]
+ test_wipe_full $DEV $bs no-dio
+done
+echo "[OK]"
+
+echo -n "[4] Wipe blocks in block device "
+for bs in 0 $MB_BYTES $((4*$MB_BYTES)); do
+ echo -n [$bs/DIO]
+ test_wipe_blocks $DEV $bs
+ echo -n [$bs]
+ test_wipe_blocks $DEV $bs no-dio
+done
+echo "[OK]"
+
+cleanup
diff --git a/tests/unit-wipe.c b/tests/unit-wipe.c
new file mode 100644
index 0000000..c3019c7
--- /dev/null
+++ b/tests/unit-wipe.c
@@ -0,0 +1,138 @@
+/*
+ * unit test helper for crypt_wipe API call
+ *
+ * Copyright (C) 2022-2023 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 <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <string.h>
+#include <sys/stat.h>
+
+#include "libcryptsetup.h"
+
+const char *test_file;
+uint64_t test_offset, test_length, test_block;
+uint32_t flags;
+crypt_wipe_pattern pattern;
+
+static void usage(void)
+{
+ fprintf(stderr, "Use:\tunit-wipe file/device zero|random|special offset length bsize [no-dio].\n");
+}
+
+static bool parse_u64(const char *arg, uint64_t *u64)
+{
+ unsigned long long ull;
+ char *end;
+
+ ull = strtoull(arg, &end, 10);
+ if (*end || !*arg || errno == ERANGE)
+ return false;
+
+ if (ull % 512)
+ return false;
+
+ *u64 = ull;
+ return true;
+}
+
+static bool parse_input_params(int argc, char **argv)
+{
+ struct stat st;
+
+ if (argc < 6 || argc > 7) {
+ usage();
+ return false;
+ }
+
+ if (stat(argv[1], &st)) {
+ fprintf(stderr, "File/device %s is missing?\n", argv[1]);
+ return false;
+ }
+ test_file = argv[1];
+
+ if (!strcmp(argv[2], "random"))
+ pattern = CRYPT_WIPE_RANDOM;
+ else if (!strcmp(argv[2], "zero"))
+ pattern = CRYPT_WIPE_ZERO;
+ else if (!strcmp(argv[2], "special"))
+ pattern = CRYPT_WIPE_SPECIAL;
+ else {
+ fprintf(stderr, "Wrong pattern specification.\n");
+ return false;
+ }
+
+ if (!parse_u64(argv[3], &test_offset)) {
+ fprintf(stderr, "Wrong offset specification.\n");
+ return false;
+ }
+
+ if (!parse_u64(argv[4], &test_length)) {
+ fprintf(stderr, "Wrong length specification.\n");
+ return false;
+ }
+
+ if (!parse_u64(argv[5], &test_block)) {
+ fprintf(stderr, "Wrong block length specification.\n");
+ return false;
+ }
+
+ if (argc > 6) {
+ if (!strcmp(argv[6], "no-dio"))
+ flags = CRYPT_WIPE_NO_DIRECT_IO;
+ else {
+ fprintf(stderr, "Wrong flags specification.\n");
+ return false;
+ }
+ }
+
+ return true;
+}
+
+int main(int argc, char **argv)
+{
+ struct crypt_device *cd;
+ int r;
+
+#ifndef NO_CRYPTSETUP_PATH
+ if (getenv("CRYPTSETUP_PATH")) {
+ printf("Cannot run this test with CRYPTSETUP_PATH set.\n");
+ exit(77);
+ }
+#endif
+
+ if (!parse_input_params(argc, argv))
+ return EXIT_FAILURE;
+
+ r = crypt_init(&cd, NULL);
+ if (r < 0) {
+ fprintf(stderr, "Context init failure %i.\n", r);
+ return EXIT_FAILURE;
+ }
+
+ r = crypt_wipe(cd, test_file, pattern, test_offset, test_length,
+ test_block, flags, NULL, NULL);
+ crypt_free(cd);
+
+ if (r)
+ fprintf(stderr, "Failure %i\n", 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..4c6a600
--- /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 +%j:%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..f3d4032
--- /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 +%j:%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
new file mode 100644
index 0000000..4b443ae
--- /dev/null
+++ b/tests/valid_header_file.xz
Binary files differ
diff --git a/tests/verity-compat-test b/tests/verity-compat-test
new file mode 100755
index 0000000..8a28a12
--- /dev/null
+++ b/tests/verity-compat-test
@@ -0,0 +1,559 @@
+#!/bin/bash
+
+[ -z "$CRYPTSETUP_PATH" ] && CRYPTSETUP_PATH=".."
+VERITYSETUP=$CRYPTSETUP_PATH/veritysetup
+VERITYSETUP_VALGRIND=../.libs/veritysetup
+VERITYSETUP_LIB_VALGRIND=../.libs
+
+DEV_NAME=verity3273
+DEV_NAME2=verity3273x
+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_NAME2 ] && dmsetup remove $DEV_NAME2 >/dev/null 2>&1
+ [ -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.roothash $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 "
+ ROOT_HASH=$($VERITYSETUP format $IMG $IMG_HASH --fec-device $FEC_DEV --fec-roots 2 -h sha256 | grep -e "Root hash" | cut -d: -f2 | tr -d "\t\n ")
+ 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
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=4096 count=1 >/dev/null 2>&1
+ 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
+ dd if=/dev/mapper/$DEV_NAME of=/dev/null bs=4096 count=1 >/dev/null 2>&1
+ 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]
+{
+ local FORMAT_PARAMS
+ local VERIFY_PARAMS
+ local ROOT_HASH
+
+ 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 root_hash_as_file in yes no; do
+ 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
+ if [ $root_hash_as_file == yes ] ; then
+ echo -n $2 > $IMG.roothash
+ FORMAT_PARAMS="$FORMAT_PARAMS --root-hash-file=$IMG.roothash"
+ VERIFY_PARAMS="$VERIFY_PARAMS --root-hash-file=$IMG.roothash"
+ ROOT_HASH=""
+ else
+ ROOT_HASH="$2"
+ fi
+
+ for fail in data hash; do
+ wipe
+ echo -n "V$4(sb=$sb root_hash_as_file=$root_hash_as_file) $5 block size $1: "
+ $VERITYSETUP format $DEV_PARAMS $FORMAT_PARAMS >$DEV_OUT
+ if [ $? -ne 0 ] ; then
+ if [[ $1 =~ "sha2" ]] ; then
+ fail "Cannot format device."
+ fi
+ return
+ fi
+
+ echo -n "[root hash]"
+ compare_out "root hash" $2
+ compare_out "salt" "$3"
+
+ $VERITYSETUP verify $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$DEV_OUT 2>&1 || fail
+ echo -n "[verify]"
+
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$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 $ROOT_HASH >>$DEV_OUT 2>&1 && \
+ fail "userspace check for $TXT corruption"
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $VERIFY_PARAMS $ROOT_HASH >>$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
+ 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 --salt=${12}"
+ elif [ -n "${12}" ]; then
+ PARAMS="$PARAMS --salt=${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
+ HASH_ORIG=$(sha256sum $IMG_TMP | cut -d' ' -f 1)
+ else
+ HASH_ORIG=$(sha256sum $1 | cut -d' ' -f 1)
+ fi
+
+ ROOT_HASH=$($VERITYSETUP format $1 $2 --fec-device=$3 $PARAMS | grep -e "Root hash" | cut -d: -f2 | tr -d "\t\n ")
+
+ 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 > /dev/null 2>&1
+
+ dd if=/dev/mapper/$DEV_NAME of=$IMG_TMP > /dev/null 2>&1
+ HASH_REPAIRED=$(sha256sum $IMG_TMP | cut -d' ' -f 1)
+
+ $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()
+{
+ command -v valgrind >/dev/null || 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]"
+
+ ROOT_HASH=$($VERITYSETUP format $IMG $HASH_DEV --fec-device $FEC $PARAMS --salt=$DEV_SALT --uuid=$DEV_UUID | grep -e "Root hash" | cut -d: -f2 | tr -d "\t\n ")
+
+ 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]"
+}
+
+function check_concurrent() # $1 hash
+{
+ DEV_PARAMS="$LOOPDEV1 $LOOPDEV2"
+
+ # First check that with two sequential opens, we are returning the expected -EEXIST
+ $VERITYSETUP format $DEV_PARAMS >/dev/null 2>&1 || fail
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $1 >/dev/null 2>&1 || fail
+ check_exists
+ $VERITYSETUP create $DEV_NAME $DEV_PARAMS $1 2>&1 >/dev/null | grep -q "Device $DEV_NAME already exists" || fail
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail
+
+ # Then do two concurrent opens, and check that libdevmapper did not return -EINVAL, which is
+ # not gracefully recoverable. Either could fail depending on scheduling, so just check that
+ # the libdevmapper error does not appear in either of the outputs.
+ cat /dev/null >$DEV_OUT
+ $VERITYSETUP create -v $DEV_NAME $DEV_PARAMS $1 >>$DEV_OUT 2>&1 &
+ $VERITYSETUP create -v $DEV_NAME $DEV_PARAMS $1 >>$DEV_OUT 2>&1 &
+ wait
+ grep -q "Command failed with code .* (wrong or missing parameters)" $DEV_OUT && fail
+ check_exists
+ rm $DEV_OUT
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1 || fail
+
+ echo "[OK]"
+}
+
+export LANG=C
+[ $(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
+
+ if check_version 1 9; then
+ echo "Verity data performance options test."
+ check_option 512 $HASH $SALT 1 sha256 "--use-tasklets" "try_verify_in_tasklet"
+ 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
+
+echo -n "Verity concurrent opening tests:"
+prepare 8192 1024
+check_concurrent 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174
+
+echo -n "Deferred removal of device:"
+prepare 8192 1024
+$VERITYSETUP format $LOOPDEV1 $IMG_HASH --format=1 --data-block-size=512 --hash-block-size=512 --hash=sha256 --salt=$SALT >/dev/null 2>&1 || fail "Cannot format device."
+$VERITYSETUP open $LOOPDEV1 $DEV_NAME $DEV $IMG_HASH 9de18652fe74edfb9b805aaed72ae2aa48f94333f1ba5c452ac33b1c39325174 || fail "Cannot activate device."
+dmsetup create $DEV_NAME2 --table "0 8 linear /dev/mapper/$DEV_NAME 0"
+[ ! -b /dev/mapper/$DEV_NAME2 ] && fail
+$VERITYSETUP close $DEV_NAME >/dev/null 2>&1 && fail
+$VERITYSETUP status $DEV_NAME >/dev/null 2>&1 || fail
+$VERITYSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+if [ $? -eq 0 ] ; then
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" || fail
+ $VERITYSETUP close --cancel-deferred $DEV_NAME >/dev/null 2>&1
+ dmsetup info $DEV_NAME | grep -q "DEFERRED REMOVE" >/dev/null 2>&1 && fail
+ $VERITYSETUP close --deferred $DEV_NAME >/dev/null 2>&1
+ dmsetup remove $DEV_NAME2 || fail
+ $VERITYSETUP status $DEV_NAME >/dev/null 2>&1 && fail
+ echo "[OK]"
+else
+ dmsetup remove $DEV_NAME2 >/dev/null 2>&1
+ $VERITYSETUP close $DEV_NAME >/dev/null 2>&1
+ echo "[N/A]"
+fi
+
+remove_mapping
+exit 0
diff --git a/tests/xfs_512_block_size.img.xz b/tests/xfs_512_block_size.img.xz
new file mode 100644
index 0000000..047c788
--- /dev/null
+++ b/tests/xfs_512_block_size.img.xz
Binary files differ