diff --git a/0003-add-test-case-for-sm4.patch b/0003-add-test-case-for-sm4.patch new file mode 100644 index 0000000000000000000000000000000000000000..fb678f9e22c72ac5ebcfe20369f6e61883f8fe03 --- /dev/null +++ b/0003-add-test-case-for-sm4.patch @@ -0,0 +1,1854 @@ +From 3b568940b0414db2fb78b1662d3e43c896df51ac Mon Sep 17 00:00:00 2001 +From: wangzhiqiang +Date: Fri, 29 Jul 2022 20:41:21 +0800 +Subject: [PATCH] [Backport]add test case for sm4-xts-plain64 + +add test case +api-test-sm.c: test cryptsetup with sm4-xts-plain64 and plain +api-test-2-sm.c: test cryptsetup with sm4-xts-plain64 and luks + +Conflict:NA +Reference:https://github.com/mbroz/cryptsetup/blob/main/tests/api-test.c + +Signed-off-by: wangzhiqiang +--- + tests/00modules-test | 1 + + tests/Makefile.am | 16 +- + tests/api-test-2-sm.c | 831 +++++++++++++++++++++++++++++++++++++ + tests/api-test-sm.c | 933 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 1780 insertions(+), 1 deletion(-) + create mode 100644 tests/api-test-2-sm.c + create mode 100644 tests/api-test-sm.c + +diff --git a/tests/00modules-test b/tests/00modules-test +index 10f52cc..a004b98 100755 +--- a/tests/00modules-test ++++ b/tests/00modules-test +@@ -41,6 +41,7 @@ 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 ++modprobe sm4_generic >/dev/null 2>&1 + + dmsetup version + +diff --git a/tests/Makefile.am b/tests/Makefile.am +index dc8fa00..125c7d8 100644 +--- a/tests/Makefile.am ++++ b/tests/Makefile.am +@@ -1,6 +1,8 @@ + TESTS = 00modules-test \ + api-test \ + api-test-2 \ ++ api-test-sm \ ++ api-test-2-sm \ + compat-test-args \ + compat-test \ + compat-test2 \ +@@ -110,6 +112,18 @@ 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 + ++api_test_sm_SOURCES = api-test-sm.c api_test.h test_utils.c ++api_test_sm_LDADD = ../libcryptsetup.la ++api_test_sm_LDFLAGS = $(AM_LDFLAGS) -static ++api_test_sm_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib ++api_test_sm_CPPFLAGS = $(AM_CPPFLAGS) -include config.h ++ ++api_test_2_sm_SOURCES = api-test-2-sm.c api_test.h test_utils.c ++api_test_2_sm_LDADD = ../libcryptsetup.la ++api_test_2_sm_LDFLAGS = $(AM_LDFLAGS) -static ++api_test_2_sm_CFLAGS = -g -Wall -O0 $(AM_CFLAGS) -I$(top_srcdir)/lib ++api_test_2_sm_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 +@@ -134,7 +148,7 @@ 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 all-symbols-test ++check_PROGRAMS = api-test api-test-2 api-test-sm api-test-2-sm differ vectors-test unit-utils-io all-symbols-test + + check-programs: $(check_PROGRAMS) fake_token_path.so + +diff --git a/tests/api-test-2-sm.c b/tests/api-test-2-sm.c +new file mode 100644 +index 0000000..73ecbeb +--- /dev/null ++++ b/tests/api-test-2-sm.c +@@ -0,0 +1,831 @@ ++/* ++ * cryptsetup library LUKS2 API check functions ++ * ++ * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2009-2021 Milan Broz ++ * Copyright (C) 2016-2021 Ondrej Kozina ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#ifdef KERNEL_KEYRING ++#include ++#include ++#ifndef HAVE_KEY_SERIAL_T ++#define HAVE_KEY_SERIAL_T ++#include ++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 "blabla" ++#define PASSPHRASE1 "albalb" ++ ++#define DEVICE_TEST_UUID "12345678-1234-1234-1234-123456789abc" ++ ++#define DEVICE_WRONG "/dev/Ooo_" ++#define DEVICE_CHAR "/dev/zero" ++#define THE_LFILE_TEMPLATE "cryptsetup-tstlp.XXXXXX" ++ ++#define KEY_DESC_TEST0 "cs_token_test:test_key0" ++#define KEY_DESC_TEST1 "cs_token_test:test_key1" ++ ++#define CONV_DIR "conversion_imgs" ++#define CONV_L1_128 "l1_128b" ++#define CONV_L1_256 "l1_256b" ++#define CONV_L1_512 "l1_512b" ++#define CONV_L2_128 "l2_128b" ++#define CONV_L2_128_FULL "l2_128b_full" ++#define CONV_L2_256 "l2_256b" ++#define CONV_L2_256_FULL "l2_256b_full" ++#define CONV_L2_512 "l2_512b" ++#define CONV_L2_512_FULL "l2_512b_full" ++#define CONV_L1_128_DET "l1_128b_det" ++#define CONV_L1_256_DET "l1_256b_det" ++#define CONV_L1_512_DET "l1_512b_det" ++#define CONV_L2_128_DET "l2_128b_det" ++#define CONV_L2_128_DET_FULL "l2_128b_det_full" ++#define CONV_L2_256_DET "l2_256b_det" ++#define CONV_L2_256_DET_FULL "l2_256b_det_full" ++#define CONV_L2_512_DET "l2_512b_det" ++#define CONV_L2_512_DET_FULL "l2_512b_det_full" ++#define CONV_L1_256_LEGACY "l1_256b_legacy_offset" ++#define CONV_L1_256_UNMOVABLE "l1_256b_unmovable" ++#define PASS0 "aaa" ++#define PASS1 "hhh" ++#define PASS2 "ccc" ++#define PASS3 "ddd" ++#define PASS4 "eee" ++#define PASS5 "fff" ++#define PASS6 "ggg" ++#define PASS7 "bbb" ++#define PASS8 "iii" ++ ++static int _fips_mode = 0; ++ ++static char *DEVICE_1 = NULL; ++static char *DEVICE_2 = NULL; ++static char *DEVICE_3 = NULL; ++static char *DEVICE_4 = NULL; ++static char *DEVICE_5 = NULL; ++static char *DEVICE_6 = NULL; ++ ++static char *tmp_file_1 = NULL; ++static char *test_loop_file = NULL; ++ ++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 int get_luks2_offsets(int metadata_device, ++ unsigned int alignpayload_sec, ++ unsigned int sector_size, ++ uint64_t *r_header_size, ++ uint64_t *r_payload_offset) ++{ ++ struct crypt_device *cd = NULL; ++ static uint64_t default_header_size = 0; ++ ++ if (!default_header_size) { ++ if (crypt_init(&cd, THE_LOOP_DEV)) ++ return -EINVAL; ++ if (crypt_format(cd, CRYPT_LUKS2, "aes", "xts-plain64", NULL, NULL, 64, NULL)) { ++ crypt_free(cd); ++ return -EINVAL; ++ } ++ ++ default_header_size = crypt_get_data_offset(cd); ++ ++ crypt_free(cd); ++ } ++ ++ if (!sector_size) ++ sector_size = 512; /* default? */ ++ ++ if ((sector_size % 512) && (sector_size % 4096)) ++ return -1; ++ ++ if (r_payload_offset) { ++ if (metadata_device) ++ *r_payload_offset = alignpayload_sec * sector_size; ++ else ++ *r_payload_offset = DIV_ROUND_UP_MODULO(default_header_size * 512, (alignpayload_sec ?: 1) * sector_size); ++ ++ *r_payload_offset /= sector_size; ++ } ++ ++ if (r_header_size) ++ *r_header_size = default_header_size; ++ ++ return 0; ++} ++ ++static 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); ++ 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); ++ 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); ++ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null", ++ tmp_file_1, TST_SECTOR_SIZE, 10); ++ if (_system(cmd, 1)) ++ return 1; ++ ++ _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1); ++ _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1); ++ ++ _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1); ++ fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro); ++ close(fd); ++ ++ _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=32 2>/dev/null", 1); ++ fd = loop_attach(&DEVICE_2, IMAGE_EMPTY, 0, 0, &ro); ++ close(fd); ++ ++ _system("dd if=/dev/zero of=" IMAGE_EMPTY_SMALL " bs=1M count=7 2>/dev/null", 1); ++ ++ _system("dd if=/dev/zero of=" IMAGE_EMPTY_SMALL_2 " bs=512 count=2050 2>/dev/null", 1); ++ ++ _system(" [ ! -e " NO_REQS_LUKS2_HEADER " ] && xz -dk " NO_REQS_LUKS2_HEADER ".xz", 1); ++ fd = loop_attach(&DEVICE_4, NO_REQS_LUKS2_HEADER, 0, 0, &ro); ++ close(fd); ++ ++ _system(" [ ! -e " REQS_LUKS2_HEADER " ] && xz -dk " REQS_LUKS2_HEADER ".xz", 1); ++ fd = loop_attach(&DEVICE_5, REQS_LUKS2_HEADER, 0, 0, &ro); ++ close(fd); ++ ++ _system(" [ ! -e " IMAGE_PV_LUKS2_SEC " ] && xz -dk " IMAGE_PV_LUKS2_SEC ".xz", 1); ++ _system(" [ ! -e " IMAGE_PV_LUKS2_SEC ".bcp ] && cp " IMAGE_PV_LUKS2_SEC " " IMAGE_PV_LUKS2_SEC ".bcp", 1); ++ fd = loop_attach(&DEVICE_6, IMAGE_PV_LUKS2_SEC, 0, 0, &ro); ++ close(fd); ++ ++ _system(" [ ! -d " CONV_DIR " ] && tar xJf " CONV_DIR ".tar.xz 2>/dev/null", 1); ++ ++ if (_system("modprobe dm-crypt >/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); ++} ++ ++ ++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 = "sm3", ++ .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 = "blabla", *passphrase2 = "nsdkFI&Y#.sd"; ++ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a818af818af"; ++ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a818af818af"; ++ size_t key_size = strlen(mk_hex) / 2; ++ const char *cipher = "sm4"; ++ const char *cipher_mode = "xts-plain64"; ++ uint64_t r_payload_offset, r_header_size, r_size_1; ++ ++ /* Cannot use Argon2 in FIPS */ ++ if (_fips_mode) { ++ pbkdf.type = CRYPT_KDF_PBKDF2; ++ pbkdf.parallel_threads = 0; ++ pbkdf.max_memory_kb = 0; ++ } ++ ++ crypt_decode_key(key, mk_hex, key_size); ++ crypt_decode_key(key3, mk_hex2, key_size); ++ ++ // init test devices ++ OK_(get_luks2_offsets(0, 0, 0, &r_header_size, &r_payload_offset)); ++ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); ++ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1)); ++ ++ ++ // format ++ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); ++ params.data_alignment = 0; ++ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Not enough space for keyslots material"); ++ CRYPT_FREE(cd); ++ ++ // test payload_offset = 0 for encrypted device with external header device ++ OK_(crypt_init(&cd, DMDIR H_DEVICE)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), 0); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = 0; ++ params.data_device = NULL; ++ ++ // test payload_offset = 0. format() should look up alignment offset from device topology ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ OK_(!(crypt_get_data_offset(cd) > 0)); ++ CRYPT_FREE(cd); ++ ++ // set_data_offset has priority, alignment must be 0 or must be compatible ++ params.data_alignment = 0; ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(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, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_8M); ++ CRYPT_FREE(cd); ++ ++ // Load gets the value from metadata ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_set_data_offset(cd, OFFSET_2M)); ++ OK_(crypt_load(cd, CRYPT_LUKS2, NULL)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_8M); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = OFFSET_4M; ++ OK_(crypt_init(&cd, DEVICE_2)); ++ 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, ¶ms), "Alignment not compatible"); ++ OK_(crypt_set_data_offset(cd, OFFSET_4M)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_4M); ++ CRYPT_FREE(cd); ++ ++ /* ++ * test limit values for backing device size ++ */ ++ params.data_alignment = OFFSET_4M; ++ OK_(get_luks2_offsets(0, params.data_alignment, 0, NULL, &r_payload_offset)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset - 1)); ++ ++ // 1 sector less than required ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_WRONG)); ++ OK_(set_fast_pbkdf(cd)); ++ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Device too small"); ++ CRYPT_FREE(cd); ++ ++ // 0 sectors for encrypted area ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small"); ++ CRYPT_FREE(cd); ++ ++ // 1 sector for encrypted area ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), r_payload_offset); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1)); ++ EQ_(r_size_1, 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, ¶ms), "Context is already formatted"); ++ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted"); ++ // change data device to wrong one ++ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small"); ++ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = 0; ++ params.data_device = DEVICE_2; ++ ++ // generate keyslot material at the end of luks header ++ OK_(crypt_init(&cd, DMDIR H_DEVICE)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7); ++ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7); ++ ++ OK_(crypt_keyslot_get_pbkdf(cd, 7, &pbkdf_tmp)); ++ OK_(strcmp(pbkdf_tmp.type, pbkdf.type)); ++ if (!_fips_mode) { ++ NULL_(pbkdf_tmp.hash); ++ OK_(!(pbkdf_tmp.max_memory_kb >= 32)); ++ OK_(!(pbkdf_tmp.parallel_threads >= 1)); ++ } else ++ OK_(strcmp(pbkdf_tmp.hash, pbkdf.hash)); ++ OK_(!(pbkdf_tmp.iterations >= 4)); ++ EQ_(0, pbkdf_tmp.time_ms); /* not usable in per-keyslot call */ ++ ++ CRYPT_FREE(cd); ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); ++ OK_(set_fast_pbkdf(cd)); ++ FAIL_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Context is already formatted"); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ CRYPT_FREE(cd); ++ // check active status without header ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ NULL_(crypt_get_type(cd)); ++ OK_(strcmp(cipher, crypt_get_cipher(cd))); ++ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd))); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = OFFSET_1M; ++ params.data_device = NULL; ++ ++ // test uuid mismatch and _init_by_name_and_header ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); ++ OK_(set_fast_pbkdf(cd)); ++ OK_(crypt_format(cd, CRYPT_LUKS2, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ EQ_(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, ¶ms)); ++ CRYPT_FREE(cd); ++ // there we've got uuid mismatch ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ NULL_(crypt_get_type(cd)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device is active"); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0), "Device is active"); ++ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_INACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ FAIL_(crypt_header_is_detached(cd), "no header for mismatched device"); ++ 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, ¶ms)); ++ ++ // even with no keyslots defined it can be activated by volume key ++ OK_(crypt_volume_key_verify(cd, key, key_size)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0)); ++ GE_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_2)); ++ ++ // now with keyslot ++ EQ_(7, crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase))); ++ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7)); ++ EQ_(7, crypt_activate_by_passphrase(cd, CDEVICE_2, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); ++ GE_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_2)); ++ ++ 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); ++ OK_(!(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); ++ 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 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"); ++ ++ _cleanup(); ++ return 0; ++} +diff --git a/tests/api-test-sm.c b/tests/api-test-sm.c +new file mode 100644 +index 0000000..cff59ce +--- /dev/null ++++ b/tests/api-test-sm.c +@@ -0,0 +1,933 @@ ++/* ++ * cryptsetup library API check functions ++ * ++ * Copyright (C) 2009-2021 Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2009-2021 Milan Broz ++ * Copyright (C) 2016-2021 Ondrej Kozina ++ * ++ * This program is free software; you can redistribute it and/or ++ * modify it under the terms of the GNU General Public License ++ * as published by the Free Software Foundation; either version 2 ++ * of the License, or (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. ++ */ ++ ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++#include ++ ++#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 "blabla" ++#define PASSPHRASE1 "albalb" ++ ++#define DEVICE_TEST_UUID "12345678-1234-1234-1234-123456789abc" ++ ++#define DEVICE_WRONG "/dev/Ooo_" ++#define DEVICE_CHAR "/dev/zero" ++#define THE_LFILE_TEMPLATE "cryptsetup-tstlp.XXXXXX" ++ ++#define LUKS_PHDR_SIZE_B 1024 ++ ++static int _fips_mode = 0; ++ ++static char *DEVICE_1 = NULL; ++static char *DEVICE_2 = NULL; ++static char *DEVICE_3 = NULL; ++ ++static char *tmp_file_1 = NULL; ++static char *test_loop_file = NULL; ++ ++struct crypt_device *cd = NULL, *cd2 = NULL; ++ ++// Helpers ++ ++static int get_luks_offsets(int metadata_device, ++ size_t keylength, ++ unsigned int alignpayload_sec, ++ unsigned int alignoffset_sec, ++ uint64_t *r_header_size, ++ uint64_t *r_payload_offset) ++{ ++ int i; ++ uint64_t current_sector; ++ uint32_t sectors_per_stripes_set; ++ ++ if (!keylength) { ++ if (r_header_size) ++ *r_header_size = 0; ++ if (r_payload_offset) ++ *r_payload_offset = 0; ++ return -1; ++ } ++ ++ sectors_per_stripes_set = DIV_ROUND_UP(keylength*LUKS_STRIPES, 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); ++ 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); ++ 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); ++ snprintf(cmd, sizeof(cmd), "dd if=/dev/zero of=%s bs=%d count=%d 2>/dev/null", ++ tmp_file_1, TST_SECTOR_SIZE, 10); ++ if (_system(cmd, 1)) ++ return 1; ++ ++ _system("dmsetup create " DEVICE_EMPTY_name " --table \"0 10000 zero\"", 1); ++ _system("dmsetup create " DEVICE_ERROR_name " --table \"0 10000 error\"", 1); ++ ++ _system(" [ ! -e " IMAGE1 " ] && xz -dk " IMAGE1 ".xz", 1); ++ fd = loop_attach(&DEVICE_1, IMAGE1, 0, 0, &ro); ++ close(fd); ++ ++ _system("dd if=/dev/zero of=" IMAGE_EMPTY " bs=1M count=10 2>/dev/null", 1); ++ fd = loop_attach(&DEVICE_2, IMAGE_EMPTY, 0, 0, &ro); ++ close(fd); ++ ++ /* Keymaterial offset is less than 8 sectors */ ++ _system(" [ ! -e " EVL_HEADER_1 " ] && xz -dk " EVL_HEADER_1 ".xz", 1); ++ /* keymaterial offset aims into payload area */ ++ _system(" [ ! -e " EVL_HEADER_2 " ] && xz -dk " EVL_HEADER_2 ".xz", 1); ++ /* keymaterial offset is valid, number of stripes causes payload area to be overwritten */ ++ _system(" [ ! -e " EVL_HEADER_3 " ] && xz -dk " EVL_HEADER_3 ".xz", 1); ++ /* luks device header for data and header on same device. payloadOffset is greater than ++ * device size (crypt_load() test) */ ++ _system(" [ ! -e " EVL_HEADER_4 " ] && xz -dk " EVL_HEADER_4 ".xz", 1); ++ /* two keyslots with same offset (overlapping keyslots) */ ++ _system(" [ ! -e " EVL_HEADER_5 " ] && xz -dk " EVL_HEADER_5 ".xz", 1); ++ /* valid header: payloadOffset=4096, key_size=32, ++ * volume_key = bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a */ ++ _system(" [ ! -e " VALID_HEADER " ] && xz -dk " VALID_HEADER ".xz", 1); ++ ++ /* Prepare tcrypt images */ ++ _system("tar xJf tcrypt-images.tar.xz 2>/dev/null", 1); ++ ++ _system("modprobe dm-crypt >/dev/null 2>&1", 0); ++ _system("modprobe dm-verity >/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); ++ ++ return 0; ++} ++ ++static void AddDevicePlain(void) ++{ ++ struct crypt_params_plain params = { ++ .hash = "sm3", ++ .skip = 0, ++ .offset = 0, ++ .size = 0 ++ }; ++ int fd; ++ char key[128], key2[128], path[128]; ++ ++ const char *passphrase = PASSPHRASE; ++ // hashed hex version of PASSPHRASE ++ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"; ++ size_t key_size = strlen(mk_hex) / 2; ++ const char *cipher = "sm4"; ++ const char *cipher_mode = "xts-plain64"; ++ ++ uint64_t size, r_size; ++ ++ crypt_decode_key(key, mk_hex, key_size); ++ FAIL_(crypt_init(&cd, ""), "empty device string"); ++ FAIL_(crypt_init(&cd, DEVICE_WRONG), "nonexistent device name "); ++ FAIL_(crypt_init(&cd, DEVICE_CHAR), "character device as backing device"); ++ OK_(crypt_init(&cd, tmp_file_1)); ++ CRYPT_FREE(cd); ++ ++ // test crypt_format, crypt_get_cipher, crypt_get_cipher_mode, crypt_get_volume_key_size ++ OK_(crypt_init(&cd,DEVICE_1)); ++ params.skip = 3; ++ params.offset = 42; ++ FAIL_(crypt_format(cd,CRYPT_PLAIN,NULL,cipher_mode,NULL,NULL,key_size,¶ms),"cipher param is null"); ++ FAIL_(crypt_format(cd,CRYPT_PLAIN,cipher,NULL,NULL,NULL,key_size,¶ms),"cipher_mode param is null"); ++ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,¶ms)); ++ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd))); ++ OK_(strcmp(cipher,crypt_get_cipher(cd))); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ EQ_(params.skip, crypt_get_iv_offset(cd)); ++ EQ_(params.offset, crypt_get_data_offset(cd)); ++ params.skip = 0; ++ params.offset = 0; ++ ++ // crypt_set_uuid() ++ FAIL_(crypt_set_uuid(cd,DEVICE_1_UUID),"can't set uuid to plain device"); ++ ++ CRYPT_FREE(cd); ++ ++ // default is "plain" hash - no password hash ++ OK_(crypt_init(&cd, DEVICE_1)); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, NULL)); ++ FAIL_(crypt_activate_by_volume_key(cd, NULL, key, key_size, 0), "cannot verify key with plain"); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ // test boundaries in offset parameter ++ t_device_size(DEVICE_1,&size); ++ params.hash = NULL; ++ // zero sectors length ++ params.offset = size >> TST_SECTOR_SHIFT; ++ OK_(crypt_init(&cd, DEVICE_1)); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd),params.offset); ++ // device size is 0 sectors ++ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0), "invalid device size (0 blocks)"); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ // data part of crypt device is of 1 sector size ++ params.offset = (size >> 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, ¶ms)); ++ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1); ++ if (t_device_size(path, &r_size) >= 0) ++ EQ_(r_size >> 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, ¶ms)); ++ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small"); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ CRYPT_FREE(cd); ++ ++ // offset == device_size (autodetect size) ++ params.offset = (size >> TST_SECTOR_SHIFT); ++ params.size = 0; ++ crypt_init(&cd, DEVICE_1); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small"); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ CRYPT_FREE(cd); ++ ++ // offset == device_size (user defined size) ++ params.offset = (size >> TST_SECTOR_SHIFT); ++ params.size = 123; ++ crypt_init(&cd, DEVICE_1); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Device too small"); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ CRYPT_FREE(cd); ++ ++ // offset+size > device_size ++ params.offset = 42; ++ params.size = (size >> TST_SECTOR_SHIFT) - params.offset + 1; ++ crypt_init(&cd, DEVICE_1); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0),"Offset and size are beyond device real size"); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ CRYPT_FREE(cd); ++ ++ // offset+size == device_size ++ params.offset = 42; ++ params.size = (size >> TST_SECTOR_SHIFT) - params.offset; ++ crypt_init(&cd, DEVICE_1); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ if (!t_device_size(path, &r_size)) ++ EQ_((r_size >> TST_SECTOR_SHIFT),params.size); ++ OK_(crypt_deactivate(cd,CDEVICE_1)); ++ ++ CRYPT_FREE(cd); ++ params.hash = "sm3"; ++ params.offset = 0; ++ params.size = 0; ++ params.skip = 0; ++ ++ // Now use hashed password ++ OK_(crypt_init(&cd, DEVICE_1)); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_passphrase(cd, NULL, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0), ++ "cannot verify passphrase with plain" ); ++ OK_(crypt_activate_by_passphrase(cd, CDEVICE_1, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); ++ ++ // device status check ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ snprintf(path, sizeof(path), "%s/%s", crypt_get_dir(), CDEVICE_1); ++ fd = open(path, O_RDONLY); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_BUSY); ++ FAIL_(crypt_deactivate(cd, CDEVICE_1), "Device is busy"); ++ close(fd); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ EQ_(crypt_status(cd, CDEVICE_1), CRYPT_INACTIVE); ++ CRYPT_FREE(cd); ++ ++ // crypt_init_by_name_and_header ++ OK_(crypt_init(&cd,DEVICE_1)); ++ OK_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ CRYPT_FREE(cd); ++ ++ // init with detached header is not supported ++ OK_(crypt_init_data_device(&cd, DEVICE_2, DEVICE_1)); ++ FAIL_(crypt_format(cd, CRYPT_PLAIN, cipher, cipher_mode, NULL, NULL, key_size, ¶ms), ++ "can't use plain with separate metadata device"); ++ CRYPT_FREE(cd); ++ ++ FAIL_(crypt_init_by_name_and_header(&cd, CDEVICE_1, H_DEVICE),"can't init plain device by header device"); ++ OK_(crypt_init_by_name(&cd, CDEVICE_1)); ++ OK_(strcmp(cipher_mode,crypt_get_cipher_mode(cd))); ++ OK_(strcmp(cipher,crypt_get_cipher(cd))); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ EQ_(params.skip, crypt_get_iv_offset(cd)); ++ EQ_(params.offset, crypt_get_data_offset(cd)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ OK_(crypt_init(&cd,DEVICE_1)); ++ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,¶ms)); ++ params.size = 0; ++ params.offset = 0; ++ ++ // crypt_set_data_device ++ FAIL_(crypt_set_data_device(cd,H_DEVICE),"can't set data device for plain device"); ++ NULL_(crypt_get_metadata_device_name(cd)); ++ 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,¶ms)); ++ 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,¶ms)); ++ OK_(crypt_activate_by_volume_key(cd,CDEVICE_1,key,key_size,0)); ++ ++ // suspend/resume tests ++ FAIL_(crypt_suspend(cd,CDEVICE_1),"cannot suspend plain device"); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ FAIL_(crypt_resume_by_passphrase(cd,CDEVICE_1,CRYPT_ANY_SLOT,passphrase, strlen(passphrase)),"cannot resume plain device"); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ ++ // retrieve volume key check ++ memset(key2, 0, key_size); ++ key_size--; ++ // small buffer ++ FAIL_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase)), "small buffer"); ++ key_size++; ++ OK_(crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase))); ++ memcpy(key, 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_(0, crypt_get_data_offset(cd)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ ++ // now with keyfile ++ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1))); ++ OK_(prepare_keyfile(KEYFILE2, KEY2, strlen(KEY2))); ++ FAIL_(crypt_activate_by_keyfile(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, 0), "cannot verify key with plain"); ++ EQ_(0, crypt_activate_by_keyfile(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ FAIL_(crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, strlen(KEY1) + 1, 0), "cannot seek"); ++ FAIL_(crypt_activate_by_keyfile_device_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, strlen(KEY1) + 1, 0), "cannot seek"); ++ EQ_(0, crypt_activate_by_keyfile_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0, 0)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ EQ_(0, crypt_activate_by_keyfile_device_offset(cd, CDEVICE_1, CRYPT_ANY_SLOT, KEYFILE1, 0, 0, 0)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ _remove_keyfiles(); ++ CRYPT_FREE(cd); ++ ++ OK_(crypt_init(&cd,DEVICE_1)); ++ OK_(crypt_format(cd,CRYPT_PLAIN,cipher,cipher_mode,NULL,NULL,key_size,¶ms)); ++ ++ // crypt_keyslot_*() ++ FAIL_(crypt_keyslot_add_by_passphrase(cd,CRYPT_ANY_SLOT,passphrase,strlen(passphrase),passphrase,strlen(passphrase)), "can't add keyslot to plain device"); ++ FAIL_(crypt_keyslot_add_by_volume_key(cd,CRYPT_ANY_SLOT ,key,key_size,passphrase,strlen(passphrase)),"can't add keyslot to plain device"); ++ FAIL_(crypt_keyslot_add_by_keyfile(cd,CRYPT_ANY_SLOT,KEYFILE1,strlen(KEY1),KEYFILE2,strlen(KEY2)),"can't add keyslot to plain device"); ++ FAIL_(crypt_keyslot_destroy(cd,1),"can't manipulate keyslots on plain device"); ++ EQ_(crypt_keyslot_status(cd, 0), CRYPT_SLOT_INVALID); ++ _remove_keyfiles(); ++ ++ CRYPT_FREE(cd); ++} ++ ++static void AddDeviceLuks(void) ++{ ++ enum { OFFSET_1M = 2048 , OFFSET_2M = 4096, OFFSET_4M = 8192, OFFSET_8M = 16384 }; ++ struct crypt_params_luks1 params = { ++ .hash = "sm3", ++ .data_alignment = OFFSET_1M, // 4M, data offset will be 4096 ++ .data_device = DEVICE_2 ++ }; ++ char key[128], key2[128], key3[128]; ++ ++ const char *passphrase = "blabla", *passphrase2 = "nsdkFI&Y#.sd"; ++ const char *mk_hex = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a"; ++ const char *mk_hex2 = "bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1e"; ++ size_t key_size = strlen(mk_hex) / 2; ++ const char *cipher = "sm4"; ++ const char *cipher_mode = "xts-plain64"; ++ uint64_t r_payload_offset, r_header_size, r_size_1; ++ struct crypt_pbkdf_type pbkdf; ++ ++ crypt_decode_key(key, mk_hex, key_size); ++ crypt_decode_key(key3, mk_hex2, key_size); ++ ++ // init test devices ++ OK_(get_luks_offsets(1, key_size, 0, 0, &r_header_size, &r_payload_offset)); ++ OK_(create_dmdevice_over_loop(H_DEVICE, r_header_size)); ++ OK_(create_dmdevice_over_loop(H_DEVICE_WRONG, r_header_size - 1)); ++ ++ // format ++ OK_(crypt_init(&cd, DMDIR H_DEVICE_WRONG)); ++ params.data_alignment = 0; ++ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Not enough space for keyslots material"); ++ CRYPT_FREE(cd); ++ ++ // test payload_offset = 0 for encrypted device with external header device ++ OK_(crypt_init(&cd, DMDIR H_DEVICE)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), 0); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = 0; ++ params.data_device = NULL; ++ ++ // test payload_offset = 0. format() should look up alignment offset from device topology ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ OK_(!(crypt_get_data_offset(cd) > 0)); ++ CRYPT_FREE(cd); ++ ++ // set_data_offset has priority, alignment must be 0 or must be compatible ++ params.data_alignment = 0; ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_set_data_offset(cd, OFFSET_8M)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_8M); ++ CRYPT_FREE(cd); ++ ++ // Load gets the value from metadata ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_set_data_offset(cd, OFFSET_2M)); ++ OK_(crypt_load(cd, CRYPT_LUKS1, NULL)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_8M); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = OFFSET_4M; ++ OK_(crypt_init(&cd, DEVICE_2)); ++ FAIL_(crypt_set_data_offset(cd, OFFSET_2M + 1), "Not aligned to 4096"); // must be aligned to 4k ++ OK_(crypt_set_data_offset(cd, OFFSET_2M)); ++ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Alignment not compatible"); ++ OK_(crypt_set_data_offset(cd, OFFSET_4M)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), OFFSET_4M); ++ CRYPT_FREE(cd); ++ ++ /* ++ * test limit values for backing device size ++ */ ++ params.data_alignment = OFFSET_2M; ++ OK_(get_luks_offsets(0, key_size, params.data_alignment, 0, NULL, &r_payload_offset)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_0S, r_payload_offset)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_1S, r_payload_offset + 1)); ++ //OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, r_payload_offset - 1)); ++ OK_(create_dmdevice_over_loop(L_DEVICE_WRONG, 2050 - 1)); //FIXME last keyslot - 1 sector ++ ++ // 1 sector less than required ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_WRONG)); ++ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Device too small"); ++ CRYPT_FREE(cd); ++ ++ // 0 sectors for encrypted area ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_0S)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Encrypted area too small"); ++ CRYPT_FREE(cd); ++ ++ // 1 sector for encrypted area ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_get_data_offset(cd), params.data_alignment); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ OK_(t_device_size(DMDIR CDEVICE_1, &r_size_1)); ++ EQ_(r_size_1, 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, ¶ms), "Context is already formatted"); ++ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, NULL), "Context is already formatted"); ++ // change data device to wrong one ++ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_0S)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device too small"); ++ OK_(crypt_set_data_device(cd, DMDIR L_DEVICE_1S)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = 0; ++ params.data_device = DEVICE_2; ++ ++ // generate keyslot material at the end of luks header ++ OK_(crypt_init(&cd, DMDIR H_DEVICE)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ EQ_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), 7); ++ EQ_(crypt_activate_by_passphrase(cd, CDEVICE_1, 7, passphrase, strlen(passphrase) ,0), 7); ++ CRYPT_FREE(cd); ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); ++ FAIL_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms), "Context is already formatted"); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ CRYPT_FREE(cd); ++ // check active status without header ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, NULL)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ NULL_(crypt_get_type(cd)); ++ OK_(strcmp(cipher, crypt_get_cipher(cd))); ++ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd))); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ CRYPT_FREE(cd); ++ ++ params.data_alignment = OFFSET_1M; ++ params.data_device = NULL; ++ ++ // test uuid mismatch and _init_by_name_and_header ++ OK_(crypt_init(&cd, DMDIR L_DEVICE_1S)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0)); ++ 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, ¶ms)); ++ CRYPT_FREE(cd); ++ // there we've got uuid mismatch ++ OK_(crypt_init_by_name_and_header(&cd, CDEVICE_1, DMDIR H_DEVICE)); ++ GE_(crypt_status(cd, CDEVICE_1), CRYPT_ACTIVE); ++ NULL_(crypt_get_type(cd)); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_1, key, key_size, 0), "Device is active"); ++ FAIL_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0), "Device is active"); ++ EQ_(crypt_status(cd, CDEVICE_2), CRYPT_INACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_1)); ++ FAIL_(crypt_header_is_detached(cd), "no header for mismatched device"); ++ CRYPT_FREE(cd); ++ ++ params.data_device = NULL; ++ ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ ++ // even with no keyslots defined it can be activated by volume key ++ OK_(crypt_volume_key_verify(cd, key, key_size)); ++ OK_(crypt_activate_by_volume_key(cd, CDEVICE_2, key, key_size, 0)); ++ GE_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_2)); ++ ++ // now with keyslot ++ EQ_(7, crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase))); ++ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7)); ++ EQ_(7, crypt_activate_by_passphrase(cd, CDEVICE_2, CRYPT_ANY_SLOT, passphrase, strlen(passphrase), 0)); ++ GE_(crypt_status(cd, CDEVICE_2), CRYPT_ACTIVE); ++ OK_(crypt_deactivate(cd, CDEVICE_2)); ++ ++ crypt_set_iteration_time(cd, 1); ++ EQ_(1, crypt_keyslot_add_by_volume_key(cd, 1, key, key_size, KEY1, strlen(KEY1))); ++ ++ // PBKDF info (in LUKS1 slots are the same) ++ FAIL_(crypt_keyslot_get_pbkdf(cd, 1, NULL), "PBKDF struct required"); ++ OK_(crypt_keyslot_get_pbkdf(cd, 1, &pbkdf)); ++ OK_(strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)); ++ OK_(strcmp(pbkdf.hash, params.hash)); ++ OK_(pbkdf.iterations < 1000); /* set by minimum iterations above */ ++ EQ_(0, pbkdf.max_memory_kb); ++ EQ_(0, pbkdf.parallel_threads); ++ FAIL_(crypt_keyslot_get_pbkdf(cd, 2, &pbkdf), "Keyslot 2 is inactive."); ++ ++ OK_(prepare_keyfile(KEYFILE1, KEY1, strlen(KEY1))); ++ OK_(prepare_keyfile(KEYFILE2, KEY2, strlen(KEY2))); ++ EQ_(2, crypt_keyslot_add_by_keyfile(cd, 2, KEYFILE1, 0, KEYFILE2, 0)); ++ FAIL_(crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 1, KEYFILE2, 0, 1), "wrong key"); ++ EQ_(3, crypt_keyslot_add_by_keyfile_offset(cd, 3, KEYFILE1, 0, 0, KEYFILE2, 0, 1)); ++ EQ_(4, crypt_keyslot_add_by_keyfile_offset(cd, 4, KEYFILE2, 0, 1, KEYFILE1, 0, 1)); ++ FAIL_(crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2)-1, 0), "key mismatch"); ++ EQ_(2, crypt_activate_by_keyfile(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 0)); ++ EQ_(3, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE2, 0, 1, 0)); ++ EQ_(4, crypt_activate_by_keyfile_offset(cd, NULL, CRYPT_ANY_SLOT, KEYFILE1, 0, 1, 0)); ++ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, strlen(KEY2), 2, 0), "not enough data"); ++ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, strlen(KEY2) + 1, 0), "cannot seek"); ++ FAIL_(crypt_activate_by_keyfile_offset(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 2, 0), "wrong key"); ++ EQ_(2, crypt_activate_by_keyfile(cd, CDEVICE_2, CRYPT_ANY_SLOT, KEYFILE2, 0, 0)); ++ OK_(crypt_keyslot_destroy(cd, 1)); ++ OK_(crypt_keyslot_destroy(cd, 2)); ++ OK_(crypt_keyslot_destroy(cd, 3)); ++ OK_(crypt_keyslot_destroy(cd, 4)); ++ OK_(crypt_deactivate(cd, CDEVICE_2)); ++ _remove_keyfiles(); ++ ++ FAIL_(crypt_keyslot_add_by_volume_key(cd, 7, key, key_size, passphrase, strlen(passphrase)), "slot used"); ++ key[1] = ~key[1]; ++ FAIL_(crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase)), "key mismatch"); ++ key[1] = ~key[1]; ++ EQ_(6, crypt_keyslot_add_by_volume_key(cd, 6, key, key_size, passphrase, strlen(passphrase))); ++ EQ_(CRYPT_SLOT_ACTIVE, crypt_keyslot_status(cd, 6)); ++ ++ FAIL_(crypt_keyslot_destroy(cd, 8), "invalid keyslot"); ++ FAIL_(crypt_keyslot_destroy(cd, CRYPT_ANY_SLOT), "invalid keyslot"); ++ FAIL_(crypt_keyslot_destroy(cd, 0), "keyslot not used"); ++ OK_(crypt_keyslot_destroy(cd, 7)); ++ EQ_(CRYPT_SLOT_INACTIVE, crypt_keyslot_status(cd, 7)); ++ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 6)); ++ ++ EQ_(7, crypt_keyslot_change_by_passphrase(cd, 6, 7, passphrase, strlen(passphrase), passphrase2, strlen(passphrase2))); ++ EQ_(CRYPT_SLOT_ACTIVE_LAST, crypt_keyslot_status(cd, 7)); ++ EQ_(7, crypt_activate_by_passphrase(cd, NULL, 7, passphrase2, strlen(passphrase2), 0)); ++ EQ_(6, crypt_keyslot_change_by_passphrase(cd, CRYPT_ANY_SLOT, 6, passphrase2, strlen(passphrase2), passphrase, strlen(passphrase))); ++ ++ EQ_(6, crypt_volume_key_get(cd, CRYPT_ANY_SLOT, key2, &key_size, passphrase, strlen(passphrase))); ++ OK_(crypt_volume_key_verify(cd, key2, key_size)); ++ ++ OK_(memcmp(key, key2, key_size)); ++ ++ OK_(strcmp(cipher, crypt_get_cipher(cd))); ++ OK_(strcmp(cipher_mode, crypt_get_cipher_mode(cd))); ++ EQ_((int)key_size, crypt_get_volume_key_size(cd)); ++ EQ_(OFFSET_2M, crypt_get_data_offset(cd)); ++ OK_(strcmp(DEVICE_2, crypt_get_device_name(cd))); ++ ++ reset_log(); ++ OK_(crypt_dump(cd)); ++ OK_(!(global_lines != 0)); ++ reset_log(); ++ FAIL_(crypt_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_deactivate(cd, CDEVICE_2), "not active"); ++ CRYPT_FREE(cd); ++ ++ // No benchmark PBKDF2 ++ pbkdf.flags = CRYPT_PBKDF_NO_BENCHMARK; ++ pbkdf.hash = "sm3"; ++ pbkdf.iterations = 1000; ++ pbkdf.time_ms = 0; ++ ++ OK_(crypt_init(&cd, DEVICE_2)); ++ OK_(crypt_set_pbkdf_type(cd, &pbkdf)); ++ OK_(crypt_format(cd, CRYPT_LUKS1, cipher, cipher_mode, NULL, key, key_size, ¶ms)); ++ CRYPT_FREE(cd); ++ ++ _cleanup_dmdevices(); ++} ++ ++ ++static void 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_(AddDevicePlain, "A plain device API creation"); ++ RUN_(AddDeviceLuks, "Format and use LUKS device"); ++ ++ _cleanup(); ++ return 0; ++} +-- +2.27.0 + diff --git a/0004-Fix-CVE-2021-4122-LUKS2-reencryption-crash-recovery-.patch b/0004-Fix-CVE-2021-4122-LUKS2-reencryption-crash-recovery-.patch new file mode 100644 index 0000000000000000000000000000000000000000..e02d4df91a72842a3a68c2443d50f082ad95f517 --- /dev/null +++ b/0004-Fix-CVE-2021-4122-LUKS2-reencryption-crash-recovery-.patch @@ -0,0 +1,863 @@ +From 0113ac2d889c5322659ad0596d4cfc6da53e356c Mon Sep 17 00:00:00 2001 +From: Ondrej Kozina +Date: Sun, 2 Jan 2022 16:57:31 +0100 +Subject: [PATCH] Fix CVE-2021-4122 - LUKS2 reencryption crash recovery attack + +Conflict:NA +Reference:https://gitlab.com/cryptsetup/cryptsetup/-/commit/0113ac2d889c5322659ad0596d4cfc6da53e356c + +Fix possible attacks against data confidentiality through LUKS2 online +reencryption extension crash recovery. + +An attacker can modify on-disk metadata to simulate decryption in +progress with crashed (unfinished) reencryption step and persistently +decrypt part of the LUKS device. + +This attack requires repeated physical access to the LUKS device but +no knowledge of user passphrases. + +The decryption step is performed after a valid user activates +the device with a correct passphrase and modified metadata. +There are no visible warnings for the user that such recovery happened +(except using the luksDump command). The attack can also be reversed +afterward (simulating crashed encryption from a plaintext) with +possible modification of revealed plaintext. + +The problem was caused by reusing a mechanism designed for actual +reencryption operation without reassessing the security impact for new +encryption and decryption operations. While the reencryption requires +calculating and verifying both key digests, no digest was needed to +initiate decryption recovery if the destination is plaintext (no +encryption key). Also, some metadata (like encryption cipher) is not +protected, and an attacker could change it. Note that LUKS2 protects +visible metadata only when a random change occurs. It does not protect +against intentional modification but such modification must not cause +a violation of data confidentiality. + +The fix introduces additional digest protection of reencryption +metadata. The digest is calculated from known keys and critical +reencryption metadata. Now an attacker cannot create correct metadata +digest without knowledge of a passphrase for used keyslots. +For more details, see LUKS2 On-Disk Format Specification version 1.1.0. +--- + lib/Makemodule.am | 1 + + lib/luks2/luks2.h | 4 + + lib/luks2/luks2_internal.h | 6 + + lib/luks2/luks2_json_metadata.c | 73 +++++- + lib/luks2/luks2_keyslot.c | 31 ++- + lib/luks2/luks2_keyslot_reenc.c | 12 +- + lib/luks2/luks2_reencrypt.c | 47 ++-- + lib/luks2/luks2_reencrypt_digest.c | 381 +++++++++++++++++++++++++++++ + lib/setup.c | 21 +- + po/POTFILES.in | 1 + + 10 files changed, 533 insertions(+), 44 deletions(-) + create mode 100644 lib/luks2/luks2_reencrypt_digest.c + +diff --git a/lib/Makemodule.am b/lib/Makemodule.am +index 5b12eae8..351dbbd3 100644 +--- a/lib/Makemodule.am ++++ b/lib/Makemodule.am +@@ -100,6 +100,7 @@ libcryptsetup_la_SOURCES = \ + lib/luks2/luks2_keyslot_luks2.c \ + lib/luks2/luks2_keyslot_reenc.c \ + lib/luks2/luks2_reencrypt.c \ ++ lib/luks2/luks2_reencrypt_digest.c \ + lib/luks2/luks2_segment.c \ + lib/luks2/luks2_token_keyring.c \ + lib/luks2/luks2_token.c \ +diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h +index fe99579d..2275cde6 100644 +--- a/lib/luks2/luks2.h ++++ b/lib/luks2/luks2.h +@@ -453,4 +453,8 @@ int LUKS2_reencrypt_check_device_size(struct crypt_device *cd, + bool activation, + bool dynamic); + ++int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks); ++ + #endif +diff --git a/lib/luks2/luks2_internal.h b/lib/luks2/luks2_internal.h +index ae6ddd51..740ba038 100644 +--- a/lib/luks2/luks2_internal.h ++++ b/lib/luks2/luks2_internal.h +@@ -239,9 +239,15 @@ int LUKS2_keyslot_reencrypt_create(struct crypt_device *cd, + int keyslot, + const struct crypt_params_reencrypt *params); + ++int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks); ++ + int LUKS2_keyslot_dump(struct crypt_device *cd, + int keyslot); + ++int LUKS2_keyslot_jobj_area(json_object *jobj_keyslot, uint64_t *offset, uint64_t *length); ++ + /* JSON helpers */ + uint64_t json_segment_get_offset(json_object *jobj_segment, unsigned blockwise); + const char *json_segment_type(json_object *jobj_segment); +diff --git a/lib/luks2/luks2_json_metadata.c b/lib/luks2/luks2_json_metadata.c +index d527f54f..0c449ee5 100644 +--- a/lib/luks2/luks2_json_metadata.c ++++ b/lib/luks2/luks2_json_metadata.c +@@ -1389,24 +1389,63 @@ int LUKS2_config_set_flags(struct crypt_device *cd, struct luks2_hdr *hdr, uint3 + */ + + /* LUKS2 library requirements */ +-static const struct { ++struct requirement_flag { + uint32_t flag; ++ uint32_t version; + const char *description; +-} requirements_flags[] = { +- { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT, "offline-reencrypt" }, +- { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, "online-reencrypt" }, +- { 0, NULL } + }; + +-static uint32_t get_requirement_by_name(const char *requirement) ++static const struct requirement_flag unknown_requirement_flag = { CRYPT_REQUIREMENT_UNKNOWN, 0, NULL }; ++ ++static const struct requirement_flag requirements_flags[] = { ++ { CRYPT_REQUIREMENT_OFFLINE_REENCRYPT,1, "offline-reencrypt" }, ++ { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 2, "online-reencrypt-v2" }, ++ { CRYPT_REQUIREMENT_ONLINE_REENCRYPT, 1, "online-reencrypt" }, ++ { 0, 0, NULL } ++}; ++ ++static const struct requirement_flag *get_requirement_by_name(const char *requirement) + { + int i; + + for (i = 0; requirements_flags[i].description; i++) + if (!strcmp(requirement, requirements_flags[i].description)) +- return requirements_flags[i].flag; ++ return requirements_flags + i; + +- return CRYPT_REQUIREMENT_UNKNOWN; ++ return &unknown_requirement_flag; ++} ++ ++static const struct requirement_flag *stored_requirement_name_by_id(struct crypt_device *cd, struct luks2_hdr *hdr, uint32_t req_id) ++{ ++ json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; ++ int i, len; ++ const struct requirement_flag *req; ++ ++ assert(hdr); ++ if (!hdr) ++ return NULL; ++ ++ if (!json_object_object_get_ex(hdr->jobj, "config", &jobj_config)) ++ return NULL; ++ ++ if (!json_object_object_get_ex(jobj_config, "requirements", &jobj_requirements)) ++ return NULL; ++ ++ if (!json_object_object_get_ex(jobj_requirements, "mandatory", &jobj_mandatory)) ++ return NULL; ++ ++ len = (int) json_object_array_length(jobj_mandatory); ++ if (len <= 0) ++ return 0; ++ ++ for (i = 0; i < len; i++) { ++ jobj = json_object_array_get_idx(jobj_mandatory, i); ++ req = get_requirement_by_name(json_object_get_string(jobj)); ++ if (req->flag == req_id) ++ return req; ++ } ++ ++ return NULL; + } + + /* +@@ -1416,7 +1455,7 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + { + json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + int i, len; +- uint32_t req; ++ const struct requirement_flag *req; + + assert(hdr); + if (!hdr || !reqs) +@@ -1443,8 +1482,8 @@ int LUKS2_config_get_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + jobj = json_object_array_get_idx(jobj_mandatory, i); + req = get_requirement_by_name(json_object_get_string(jobj)); + log_dbg(cd, "%s - %sknown", json_object_get_string(jobj), +- reqs_unknown(req) ? "un" : ""); +- *reqs |= req; ++ reqs_unknown(req->flag) ? "un" : ""); ++ *reqs |= req->flag; + } + + return 0; +@@ -1454,6 +1493,8 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + { + json_object *jobj_config, *jobj_requirements, *jobj_mandatory, *jobj; + int i, r = -EINVAL; ++ const struct requirement_flag *req; ++ uint32_t req_id; + + if (!hdr) + return -EINVAL; +@@ -1463,8 +1504,14 @@ int LUKS2_config_set_requirements(struct crypt_device *cd, struct luks2_hdr *hdr + return -ENOMEM; + + for (i = 0; requirements_flags[i].description; i++) { +- if (reqs & requirements_flags[i].flag) { +- jobj = json_object_new_string(requirements_flags[i].description); ++ req_id = reqs & requirements_flags[i].flag; ++ if (req_id) { ++ /* retain already stored version of requirement flag */ ++ req = stored_requirement_name_by_id(cd, hdr, req_id); ++ if (req) ++ jobj = json_object_new_string(req->description); ++ else ++ jobj = json_object_new_string(requirements_flags[i].description); + if (!jobj) { + r = -ENOMEM; + goto err; +diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c +index 8534a850..3d701868 100644 +--- a/lib/luks2/luks2_keyslot.c ++++ b/lib/luks2/luks2_keyslot.c +@@ -288,19 +288,9 @@ crypt_keyslot_info LUKS2_keyslot_info(struct luks2_hdr *hdr, int keyslot) + return CRYPT_SLOT_ACTIVE; + } + +-int LUKS2_keyslot_area(struct luks2_hdr *hdr, +- int keyslot, +- uint64_t *offset, +- uint64_t *length) ++int LUKS2_keyslot_jobj_area(json_object *jobj_keyslot, uint64_t *offset, uint64_t *length) + { +- json_object *jobj_keyslot, *jobj_area, *jobj; +- +- if(LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID) +- return -EINVAL; +- +- jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); +- if (!jobj_keyslot) +- return -ENOENT; ++ json_object *jobj_area, *jobj; + + if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) + return -EINVAL; +@@ -316,6 +306,23 @@ int LUKS2_keyslot_area(struct luks2_hdr *hdr, + return 0; + } + ++int LUKS2_keyslot_area(struct luks2_hdr *hdr, ++ int keyslot, ++ uint64_t *offset, ++ uint64_t *length) ++{ ++ json_object *jobj_keyslot; ++ ++ if (LUKS2_keyslot_info(hdr, keyslot) == CRYPT_SLOT_INVALID) ++ return -EINVAL; ++ ++ jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot); ++ if (!jobj_keyslot) ++ return -ENOENT; ++ ++ return LUKS2_keyslot_jobj_area(jobj_keyslot, offset, length); ++} ++ + static int _open_and_verify(struct crypt_device *cd, + struct luks2_hdr *hdr, + const keyslot_handler *h, +diff --git a/lib/luks2/luks2_keyslot_reenc.c b/lib/luks2/luks2_keyslot_reenc.c +index 34eefbf8..adec5734 100644 +--- a/lib/luks2/luks2_keyslot_reenc.c ++++ b/lib/luks2/luks2_keyslot_reenc.c +@@ -176,9 +176,17 @@ static int reenc_keyslot_store(struct crypt_device *cd, + return r < 0 ? r : keyslot; + } + +-static int reenc_keyslot_wipe(struct crypt_device *cd __attribute__((unused)), +- int keyslot __attribute__((unused))) ++static int reenc_keyslot_wipe(struct crypt_device *cd, ++ int keyslot) + { ++ struct luks2_hdr *hdr; ++ ++ if (!(hdr = crypt_get_hdr(cd, CRYPT_LUKS2))) ++ return -EINVAL; ++ ++ /* remove reencryption verification data */ ++ LUKS2_digest_assign(cd, hdr, keyslot, CRYPT_ANY_DIGEST, 0, 0); ++ + return 0; + } + +diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c +index d0e0dc40..f75ff825 100644 +--- a/lib/luks2/luks2_reencrypt.c ++++ b/lib/luks2/luks2_reencrypt.c +@@ -95,6 +95,7 @@ struct luks2_reencrypt { + static int reencrypt_keyslot_update(struct crypt_device *cd, + const struct luks2_reencrypt *rh) + { ++ int r; + json_object *jobj_keyslot, *jobj_area, *jobj_area_type; + struct luks2_hdr *hdr; + +@@ -124,7 +125,11 @@ static int reencrypt_keyslot_update(struct crypt_device *cd, + } else + log_dbg(cd, "No update of reencrypt keyslot needed."); + +- return 0; ++ r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, rh->vks); ++ if (r < 0) ++ log_err(cd, "Failed to refresh reencryption verification digest."); ++ ++ return r; + } + + static json_object *reencrypt_segment(struct luks2_hdr *hdr, unsigned new) +@@ -2484,6 +2489,10 @@ static int reencrypt_init(struct crypt_device *cd, + if (r < 0) + goto out; + ++ r = LUKS2_keyslot_reencrypt_digest_create(cd, hdr, *vks); ++ if (r < 0) ++ goto out; ++ + if (name && params->mode != CRYPT_REENCRYPT_ENCRYPT) { + r = reencrypt_verify_and_upload_keys(cd, hdr, LUKS2_reencrypt_digest_old(hdr), LUKS2_reencrypt_digest_new(hdr), *vks); + if (r) +@@ -2614,20 +2623,28 @@ static int reencrypt_context_update(struct crypt_device *cd, + static int reencrypt_load(struct crypt_device *cd, struct luks2_hdr *hdr, + uint64_t device_size, + const struct crypt_params_reencrypt *params, ++ struct volume_key *vks, + struct luks2_reencrypt **rh) + { + int r; + struct luks2_reencrypt *tmp = NULL; + crypt_reencrypt_info ri = LUKS2_reencrypt_status(hdr); + ++ if (ri == CRYPT_REENCRYPT_NONE) { ++ log_err(cd, _("Device not marked for LUKS2 reencryption.")); ++ return -EINVAL; ++ } else if (ri == CRYPT_REENCRYPT_INVALID) ++ return -EINVAL; ++ ++ r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ if (r < 0) ++ return r; ++ + if (ri == CRYPT_REENCRYPT_CLEAN) + r = reencrypt_load_clean(cd, hdr, device_size, &tmp, params); + else if (ri == CRYPT_REENCRYPT_CRASH) + r = reencrypt_load_crashed(cd, hdr, device_size, &tmp); +- else if (ri == CRYPT_REENCRYPT_NONE) { +- log_err(cd, _("Device not marked for LUKS2 reencryption.")); +- return -EINVAL; +- } else ++ else + r = -EINVAL; + + if (r < 0 || !tmp) { +@@ -2876,7 +2893,7 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd, + rparams.device_size = required_size; + } + +- r = reencrypt_load(cd, hdr, device_size, &rparams, &rh); ++ r = reencrypt_load(cd, hdr, device_size, &rparams, *vks, &rh); + if (r < 0 || !rh) + goto err; + +@@ -3096,13 +3113,6 @@ static reenc_status_t reencrypt_step(struct crypt_device *cd, + { + int r; + +- /* update reencrypt keyslot protection parameters in memory only */ +- r = reencrypt_keyslot_update(cd, rh); +- if (r < 0) { +- log_dbg(cd, "Keyslot update failed."); +- return REENC_ERR; +- } +- + /* in memory only */ + r = reencrypt_make_segments(cd, hdr, rh, device_size); + if (r) +@@ -3370,6 +3380,15 @@ int crypt_reencrypt_run( + + rs = REENC_OK; + ++ /* update reencrypt keyslot protection parameters in memory only */ ++ if (!quit && (rh->device_size > rh->progress)) { ++ r = reencrypt_keyslot_update(cd, rh); ++ if (r < 0) { ++ log_dbg(cd, "Keyslot update failed."); ++ return reencrypt_teardown(cd, hdr, rh, REENC_ERR, quit, progress, usrptr); ++ } ++ } ++ + while (!quit && (rh->device_size > rh->progress)) { + rs = reencrypt_step(cd, hdr, rh, rh->device_size, rh->online); + if (rs != REENC_OK) +@@ -3409,7 +3428,7 @@ static int reencrypt_recovery(struct crypt_device *cd, + int r; + struct luks2_reencrypt *rh = NULL; + +- r = reencrypt_load(cd, hdr, device_size, NULL, &rh); ++ r = reencrypt_load(cd, hdr, device_size, NULL, vks, &rh); + if (r < 0) { + log_err(cd, _("Failed to load LUKS2 reencryption context.")); + return r; +diff --git a/lib/luks2/luks2_reencrypt_digest.c b/lib/luks2/luks2_reencrypt_digest.c +new file mode 100644 +index 00000000..7ee277cb +--- /dev/null ++++ b/lib/luks2/luks2_reencrypt_digest.c +@@ -0,0 +1,381 @@ ++/* ++ * LUKS - Linux Unified Key Setup v2, reencryption digest helpers ++ * ++ * Copyright (C) 2022, Red Hat, Inc. All rights reserved. ++ * Copyright (C) 2022, Ondrej Kozina ++ * Copyright (C) 2022, 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 "luks2_internal.h" ++#include ++ ++#define MAX_STR 64 ++ ++struct jtype { ++ enum { JNONE = 0, JSTR, JU64, JX64, JU32 } type; ++ json_object *jobj; ++ const char *id; ++}; ++ ++static size_t sr(struct jtype *j, uint8_t *ptr) ++{ ++ json_object *jobj; ++ size_t len = 0; ++ uint64_t u64; ++ uint32_t u32; ++ ++ if (!json_object_is_type(j->jobj, json_type_object)) ++ return 0; ++ ++ if (!json_object_object_get_ex(j->jobj, j->id, &jobj)) ++ return 0; ++ ++ switch(j->type) { ++ case JSTR: /* JSON string */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ return 0; ++ len = strlen(json_object_get_string(jobj)); ++ if (len > MAX_STR) ++ return 0; ++ if (ptr) ++ memcpy(ptr, json_object_get_string(jobj), len); ++ break; ++ case JU64: /* Unsigned 64bit integer stored as string */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ break; ++ len = sizeof(u64); ++ if (ptr) { ++ u64 = cpu_to_be64(crypt_jobj_get_uint64(jobj)); ++ memcpy(ptr, &u64, len); ++ } ++ break; ++ case JX64: /* Unsigned 64bit segment size (allows "dynamic") */ ++ if (!json_object_is_type(jobj, json_type_string)) ++ break; ++ if (!strcmp(json_object_get_string(jobj), "dynamic")) { ++ len = strlen("dynamic"); ++ if (ptr) ++ memcpy(ptr, json_object_get_string(jobj), len); ++ } else { ++ len = sizeof(u64); ++ u64 = cpu_to_be64(crypt_jobj_get_uint64(jobj)); ++ if (ptr) ++ memcpy(ptr, &u64, len); ++ } ++ break; ++ case JU32: /* Unsigned 32bit integer, stored as JSON int */ ++ if (!json_object_is_type(jobj, json_type_int)) ++ return 0; ++ len = sizeof(u32); ++ if (ptr) { ++ u32 = cpu_to_be32(crypt_jobj_get_uint32(jobj)); ++ memcpy(ptr, &u32, len); ++ } ++ break; ++ case JNONE: ++ return 0; ++ }; ++ ++ return len; ++} ++ ++static size_t srs(struct jtype j[], uint8_t *ptr) ++{ ++ size_t l, len = 0; ++ ++ while(j->jobj) { ++ l = sr(j, ptr); ++ if (!l) ++ return 0; ++ len += l; ++ if (ptr) ++ ptr += l; ++ j++; ++ } ++ return len; ++} ++ ++static size_t segment_linear_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ struct jtype j[] = { ++ { JSTR, jobj_segment, "type" }, ++ { JU64, jobj_segment, "offset" }, ++ { JX64, jobj_segment, "size" }, ++ {} ++ }; ++ return srs(j, buffer); ++} ++ ++static size_t segment_crypt_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ struct jtype j[] = { ++ { JSTR, jobj_segment, "type" }, ++ { JU64, jobj_segment, "offset" }, ++ { JX64, jobj_segment, "size" }, ++ { JU64, jobj_segment, "iv_tweak" }, ++ { JSTR, jobj_segment, "encryption" }, ++ { JU32, jobj_segment, "sector_size" }, ++ {} ++ }; ++ return srs(j, buffer); ++} ++ ++static size_t segment_serialize(json_object *jobj_segment, uint8_t *buffer) ++{ ++ json_object *jobj_type; ++ const char *segment_type; ++ ++ if (!json_object_object_get_ex(jobj_segment, "type", &jobj_type)) ++ return 0; ++ ++ if (!(segment_type = json_object_get_string(jobj_type))) ++ return 0; ++ ++ if (!strcmp(segment_type, "crypt")) ++ return segment_crypt_serialize(jobj_segment, buffer); ++ else if (!strcmp(segment_type, "linear")) ++ return segment_linear_serialize(jobj_segment, buffer); ++ ++ return 0; ++} ++ ++static size_t backup_segments_serialize(struct luks2_hdr *hdr, uint8_t *buffer) ++{ ++ json_object *jobj_segment; ++ size_t l, len = 0; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-previous"); ++ if (!jobj_segment || !(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ if (buffer) ++ buffer += l; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-final"); ++ if (!jobj_segment || !(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ if (buffer) ++ buffer += l; ++ ++ jobj_segment = LUKS2_get_segment_by_flag(hdr, "backup-moved-segment"); ++ if (jobj_segment) { ++ if (!(l = segment_serialize(jobj_segment, buffer))) ++ return 0; ++ len += l; ++ } ++ ++ return len; ++} ++ ++static size_t reenc_keyslot_serialize(struct luks2_hdr *hdr, uint8_t *buffer) ++{ ++ json_object *jobj_keyslot, *jobj_area, *jobj_type; ++ const char *area_type; ++ int keyslot_reencrypt; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return 0; ++ ++ if (!(jobj_keyslot = LUKS2_get_keyslot_jobj(hdr, keyslot_reencrypt))) ++ return 0; ++ ++ if (!json_object_object_get_ex(jobj_keyslot, "area", &jobj_area)) ++ return 0; ++ ++ if (!json_object_object_get_ex(jobj_area, "type", &jobj_type)) ++ return 0; ++ ++ if (!(area_type = json_object_get_string(jobj_type))) ++ return 0; ++ ++ struct jtype j[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ {} ++ }; ++ struct jtype j_datashift[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ { JU64, jobj_area, "shift_size" }, ++ {} ++ }; ++ struct jtype j_checksum[] = { ++ { JSTR, jobj_keyslot, "mode" }, ++ { JSTR, jobj_keyslot, "direction" }, ++ { JSTR, jobj_area, "type" }, ++ { JU64, jobj_area, "offset" }, ++ { JU64, jobj_area, "size" }, ++ { JSTR, jobj_area, "hash" }, ++ { JU32, jobj_area, "sector_size" }, ++ {} ++ }; ++ ++ if (!strcmp(area_type, "datashift")) ++ return srs(j_datashift, buffer); ++ else if (!strcmp(area_type, "checksum")) ++ return srs(j_checksum, buffer); ++ ++ return srs(j, buffer); ++} ++ ++static size_t blob_serialize(void *blob, size_t length, uint8_t *buffer) ++{ ++ if (buffer) ++ memcpy(buffer, blob, length); ++ ++ return length; ++} ++ ++static int reencrypt_assembly_verification_data(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks, ++ struct volume_key **verification_data) ++{ ++ uint8_t *ptr; ++ int digest_new, digest_old; ++ struct volume_key *data = NULL, *vk_old = NULL, *vk_new = NULL; ++ size_t keyslot_data_len, segments_data_len, data_len = 2; ++ ++ /* Keys - calculate length */ ++ digest_new = LUKS2_reencrypt_digest_new(hdr); ++ digest_old = LUKS2_reencrypt_digest_old(hdr); ++ ++ if (digest_old >= 0) { ++ vk_old = crypt_volume_key_by_id(vks, digest_old); ++ if (!vk_old) ++ return -EINVAL; ++ data_len += blob_serialize(vk_old->key, vk_old->keylength, NULL); ++ } ++ ++ if (digest_new >= 0 && digest_old != digest_new) { ++ vk_new = crypt_volume_key_by_id(vks, digest_new); ++ if (!vk_new) ++ return -EINVAL; ++ data_len += blob_serialize(vk_new->key, vk_new->keylength, NULL); ++ } ++ ++ if (data_len == 2) ++ return -EINVAL; ++ ++ /* Metadata - calculate length */ ++ if (!(keyslot_data_len = reenc_keyslot_serialize(hdr, NULL))) ++ return -EINVAL; ++ data_len += keyslot_data_len; ++ ++ if (!(segments_data_len = backup_segments_serialize(hdr, NULL))) ++ return -EINVAL; ++ data_len += segments_data_len; ++ ++ /* Alloc and fill serialization data */ ++ data = crypt_alloc_volume_key(data_len, NULL); ++ if (!data) ++ return -ENOMEM; ++ ++ ptr = (uint8_t*)data->key; ++ ++ /* v2 */ ++ *ptr++ = 0x76; ++ *ptr++ = 0x32; ++ ++ if (vk_old) ++ ptr += blob_serialize(vk_old->key, vk_old->keylength, ptr); ++ ++ if (vk_new) ++ ptr += blob_serialize(vk_new->key, vk_new->keylength, ptr); ++ ++ if (!reenc_keyslot_serialize(hdr, ptr)) ++ goto bad; ++ ptr += keyslot_data_len; ++ ++ if (!backup_segments_serialize(hdr, ptr)) ++ goto bad; ++ ptr += segments_data_len; ++ ++ assert((size_t)(ptr - (uint8_t*)data->key) == data_len); ++ ++ *verification_data = data; ++ ++ return 0; ++bad: ++ crypt_free_volume_key(data); ++ return -EINVAL; ++} ++ ++int LUKS2_keyslot_reencrypt_digest_create(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks) ++{ ++ int digest_reencrypt, keyslot_reencrypt, r; ++ struct volume_key *data; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return keyslot_reencrypt; ++ ++ r = reencrypt_assembly_verification_data(cd, hdr, vks, &data); ++ if (r < 0) ++ return r; ++ ++ r = LUKS2_digest_create(cd, "pbkdf2", hdr, data); ++ crypt_free_volume_key(data); ++ if (r < 0) ++ return r; ++ ++ digest_reencrypt = r; ++ ++ r = LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, CRYPT_ANY_DIGEST, 0, 0); ++ if (r < 0) ++ return r; ++ ++ return LUKS2_digest_assign(cd, hdr, keyslot_reencrypt, digest_reencrypt, 1, 0); ++} ++ ++int LUKS2_reencrypt_digest_verify(struct crypt_device *cd, ++ struct luks2_hdr *hdr, ++ struct volume_key *vks) ++{ ++ int r, keyslot_reencrypt; ++ struct volume_key *data; ++ ++ keyslot_reencrypt = LUKS2_find_keyslot(hdr, "reencrypt"); ++ if (keyslot_reencrypt < 0) ++ return keyslot_reencrypt; ++ ++ r = reencrypt_assembly_verification_data(cd, hdr, vks, &data); ++ if (r < 0) ++ return r; ++ ++ r = LUKS2_digest_verify(cd, hdr, data, keyslot_reencrypt); ++ crypt_free_volume_key(data); ++ ++ if (r < 0) { ++ if (r == -ENOENT) ++ log_dbg(cd, "Reencryption digest is missing."); ++ log_err(cd, _("Reencryption metadata is invalid.")); ++ } else ++ log_dbg(cd, "Reencryption metadata verified."); ++ ++ return r; ++} +diff --git a/lib/setup.c b/lib/setup.c +index a5dfd843..86a932da 100644 +--- a/lib/setup.c ++++ b/lib/setup.c +@@ -4131,6 +4131,12 @@ static int _open_and_activate_reencrypt_device(struct crypt_device *cd, + keyslot = r; + } + ++ if (r >= 0) { ++ r = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ if (r < 0) ++ goto out; ++ } ++ + log_dbg(cd, "Entering clean reencryption state mode."); + + if (r >= 0) +@@ -4158,8 +4164,9 @@ static int _open_and_activate_luks2(struct crypt_device *cd, + uint32_t flags) + { + crypt_reencrypt_info ri; +- int r; ++ int r, rv; + struct luks2_hdr *hdr = &cd->u.luks2.hdr; ++ struct volume_key *vks = NULL; + + ri = LUKS2_reencrypt_status(hdr); + if (ri == CRYPT_REENCRYPT_INVALID) +@@ -4169,9 +4176,17 @@ static int _open_and_activate_luks2(struct crypt_device *cd, + if (name) + r = _open_and_activate_reencrypt_device(cd, hdr, keyslot, name, passphrase, + passphrase_size, flags); +- else ++ else { + r = _open_all_keys(cd, hdr, keyslot, passphrase, +- passphrase_size, flags, NULL); ++ passphrase_size, flags, &vks); ++ if (r < 0) ++ return r; ++ ++ rv = LUKS2_reencrypt_digest_verify(cd, hdr, vks); ++ crypt_free_volume_key(vks); ++ if (rv < 0) ++ return rv; ++ } + } else + r = _open_and_activate(cd, keyslot, name, passphrase, + passphrase_size, flags); +diff --git a/po/POTFILES.in b/po/POTFILES.in +index 867d879d..dac9a31f 100644 +--- a/po/POTFILES.in ++++ b/po/POTFILES.in +@@ -37,6 +37,7 @@ lib/luks2/luks2_keyslot_luks2.c + lib/luks2/luks2_keyslot_reenc.c + lib/luks2/luks2_luks1_convert.c + lib/luks2/luks2_reencrypt.c ++lib/luks2/luks2_reencrypt_digest.c + lib/luks2/luks2_segment.c + lib/luks2/luks2_token.c + lib/luks2/luks2_token_keyring.c +-- +2.27.0 + diff --git a/0005-check-whether-the-forced-iteration-count-is-out-of-r.patch b/0005-check-whether-the-forced-iteration-count-is-out-of-r.patch new file mode 100644 index 0000000000000000000000000000000000000000..c7cb22dccbb3ff826969b1bb66ba162edbf61437 --- /dev/null +++ b/0005-check-whether-the-forced-iteration-count-is-out-of-r.patch @@ -0,0 +1,58 @@ +From 3592f3da11ba6d830d700c2c987165ddc86345d8 Mon Sep 17 00:00:00 2001 +From: wangzhiqiang +Date: Mon, 30 Jan 2023 16:36:29 +0800 +Subject: [PATCH] check whether the forced iteration count is out of range + +struct crypt_pbkdf_type has a uint32_t variable iterations, but +PKCS5_PBKDF2_HMAC interface of openssl accept int variable, so +return fail when it greater than INT_MAX. + +Signed-off-by: wangzhiqiang +--- + lib/crypto_backend/crypto_openssl.c | 2 +- + lib/luks2/luks2_keyslot_luks2.c | 1 + + man/cryptsetup.8 | 3 ++- + 3 files changed, 4 insertions(+), 2 deletions(-) + +diff --git a/lib/crypto_backend/crypto_openssl.c b/lib/crypto_backend/crypto_openssl.c +index 0dbcb75..db400e4 100644 +--- a/lib/crypto_backend/crypto_openssl.c ++++ b/lib/crypto_backend/crypto_openssl.c +@@ -541,7 +541,7 @@ int crypt_pbkdf(const char *kdf, const char *hash, + uint32_t iterations, uint32_t memory, uint32_t parallel) + + { +- if (!kdf) ++ if (!kdf || iterations > INT_MAX) + return -EINVAL; + + if (!strcmp(kdf, "pbkdf2")) { +diff --git a/lib/luks2/luks2_keyslot_luks2.c b/lib/luks2/luks2_keyslot_luks2.c +index ea58112..509a391 100644 +--- a/lib/luks2/luks2_keyslot_luks2.c ++++ b/lib/luks2/luks2_keyslot_luks2.c +@@ -254,6 +254,7 @@ static int luks2_keyslot_set_key(struct crypt_device *cd, + pbkdf.iterations, pbkdf.max_memory_kb, + pbkdf.parallel_threads); + if (r < 0) { ++ log_err(cd, "Invalid parameter."); + crypt_free_volume_key(derived_key); + return r; + } +diff --git a/man/cryptsetup.8 b/man/cryptsetup.8 +index c54480a..70d1f23 100644 +--- a/man/cryptsetup.8 ++++ b/man/cryptsetup.8 +@@ -1132,7 +1132,8 @@ be used on some small embedded system. + + \fBMINIMAL AND MAXIMAL PBKDF COSTS:\fR + For \fBPBKDF2\fR, the minimum iteration count is 1000 and +-maximum is 4294967295 (maximum for 32bit unsigned integer). ++maximum is 4294967295 (maximum for 32bit unsigned integer), ++except openssl, which supports only 2147483647 (maximum for 32bit integer). + Memory and parallel costs are unused for PBKDF2. + For \fBArgon2i\fR and \fBArgon2id\fR, minimum iteration count (CPU cost) is 4 and + maximum is 4294967295 (maximum for 32bit unsigned integer). +-- +2.33.0 + diff --git a/cryptsetup.spec b/cryptsetup.spec index 7cfd84a4816e9fa0179e1031e2d9ac378294a7c4..d5503a021e14533b5a233b275e5d94ca274cf1b0 100644 --- a/cryptsetup.spec +++ b/cryptsetup.spec @@ -1,6 +1,6 @@ Name: cryptsetup Version: 2.4.1 -Release: 2 +Release: 3 Summary: Utility used to conveniently set up disk encryption License: GPLv2+ and CC0-1.0 and LGPLv2+ URL: https://gitlab.com/cryptsetup/cryptsetup @@ -8,8 +8,11 @@ Source0: https://www.kernel.org/pub/linux/utils/cryptsetup/v2.4/cryptsetup-%{ve Patch1: 0001-cryptsetup-add-system-library-paths.patch Patch2: 0002-fix-compat-test.patch +Patch3: 0003-add-test-case-for-sm4.patch +Patch4: 0004-Fix-CVE-2021-4122-LUKS2-reencryption-crash-recovery-.patch +Patch5: 0005-check-whether-the-forced-iteration-count-is-out-of-r.patch -BuildRequires: openssl-devel, popt-devel, device-mapper-devel, gcc, libssh-devel +BuildRequires: openssl-devel, popt-devel, device-mapper-devel, gcc, libssh-devel, gettext-devel libtool BuildRequires: libuuid-devel, json-c-devel, libargon2-devel, libpwquality-devel, libblkid-devel Requires: libpwquality >= 1.2.0 @@ -67,6 +70,7 @@ This contains man files for the using of cryptsetup. %autosetup -n %{name}-%{version} -p1 %build +./autogen.sh %configure --enable-fips --enable-pwquality --enable-libargon2 --with-crypto_backend=openssl %make_build @@ -113,6 +117,9 @@ make check %{_mandir}/man8/* %changelog +* Thu Feb 23 2023 volcanodragon - 2.4.1-3 +- add testcase for sm4-xts-plain64, fix CVE-2021-4122, add out of range check + * Mon Oct 17 2022 wuguanghao - 2.4.1-2 - add CC0-1.0 license