[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#985629: Bug#981405: unblock (pre-approval): cryptsetup/2:2.3.5-1



On Sun, 21 Mar 2021 at 00:51:06 +0100, Guilhem Moulin wrote:
> I'm seeking pre-approval to upload cryptsetup/2:2.3.5-1 to unstable.
> Hopefully the attached debdiff is not too large/intrusive for inclusion
> in Bullseye.  For convenience I'm also attaching a diff with the ‘tests’
> and ‘po’ directories (which arguably add a lot of clutter), as well as a
> diff with only upstream's ‘lib’ and ‘src’ directories (given it's really
> what this is about).

Ooops Savaltore pointed out that the message wasn't delivered to
debian-release@l.d.o due to its attachment size.  Not a good sign
perhaps, although most of the clutter is due to offset updates in .po
file annotations…

The original attachments can be found at https://bugs.debian.org/985629 ,
but here is a new try with only .debdiff-trimmed (source tree diff
excluding ‘tests’ and ‘po’ directories) , as well as the output of `git
diff -b debian/2%2.3.4-2 -- lib src debian` *after* reverting upstream's
copyright year update in headings (so preserving only upstream code
changes and debian changes).

-- 
Guilhem.

Attachment: cryptsetup.debdiff-trimmed.gz
Description: application/gzip

diff --git a/debian/README.source b/debian/README.source
index 15c40923..a24270ac 100644
--- a/debian/README.source
+++ b/debian/README.source
@@ -21,31 +21,19 @@ Importing a new upstream release
            git remote add upstream https://gitlab.com/cryptsetup/cryptsetup.git
            git config remote.upstream.fetch +refs/heads/master:refs/remotes/upstream/master
            git branch -u refs/remotes/upstream/master upstream/latest
+           git config remote.upstream.tagOpt --tags
 
-    1. Update 'upstream' remote
-
-           git fetch --tags upstream
-
-    2. Determine the release tag corresponding to the new release. At the time
-       of this writing, upstream uses tags in the form:
-
-           TAG="v$VERSION"
-
-       This convention may change, so double-check with 'git tag'.
-
-    3. Validate the gpg signature for this release tag:
-
-           git verify-tag "$TAG"
-
-       The signing key can be found in 'debian/upstream-signing-key.asc'.
-       Use `gpg -q debian/upstream-signing-key.asc` to show its
-       fingerprint.
-
-    4. Merge the upstream release tag <tag> into the 'debian/latest'
+    1. Merge the newest upstream release tag (pass --upstream-version=$VERSION
+       if you want a specific upstream version) into the 'debian/latest'
        branch of your packaging repository:
 
-           git checkout debian/latest
-           git merge -m "Updated version $VERSION from '$TAG'" "$TAG"
+           gbp import-orig --uscan
+
+       That commands does all the magic, namely
+       - updating the `upstream` remote,
+       - verifying the cryptographic signature on the upstream tag 'v$VERSION',
+       - creating a new tag 'upstream/$VERSION' with 'v$VERSION' as additional parent, and
+       - merging 'upstream/$VERSION' it into 'debian/latest'
 
     N. After development and testing, the final packages to be uploaded to
        Debian are built and tagged in the repository as follows:
diff --git a/debian/changelog b/debian/changelog
index cf73b180..9f71768f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,18 @@
+cryptsetup (2:2.3.5-1) unstable; urgency=medium
+
+  * New upstream bugfix release.  (Closes: #985581)
+  * d/watch: Monitor upstream tags rather than tarballs.
+  * d/gbp.conf: Set 'upstream-vcs-tag' to add upstream tag as additional
+    parent.
+  * Simplify d/README.source in accordance with the above.
+  * Rename d/upstream-signing-key.asc to d/upstream/signing-key.asc as uscan
+    is now able to verify git tags.
+  * encrypted-boot.md: Clarify how to solve double password prompt for the
+    device holding /boot.
+  * d/copyright: Update copyright year.
+
+ -- Guilhem Moulin <guilhem@debian.org>  Thu, 11 Mar 2021 23:33:13 +0100
+
 cryptsetup (2:2.3.4-2) unstable; urgency=medium
 
   [ Guilhem Moulin ]
diff --git a/debian/copyright b/debian/copyright
index 058d0c35..8ce5f177 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -5,8 +5,8 @@ Source: https://gitlab.com/cryptsetup/cryptsetup
 Files: *
 Copyright: © 2004      Christophe Saout <christophe@saout.de>
            © 2004-2008 Clemens Fruhwirth <clemens@endorphin.org>
-           © 2008-2019 Red Hat, Inc.
-           © 2008-2019 Milan Broz <gmazyland@gmail.com>
+           © 2008-2021 Red Hat, Inc.
+           © 2008-2021 Milan Broz <gmazyland@gmail.com>
 License: GPL-2+ with OpenSSL exception
 
 Files: debian/*
@@ -45,7 +45,7 @@ Copyright: © 2005-2015 Jonas Meurer <jonas@freesources.org>
 License: GPL-2+
 
 Files: docs/examples/*
-Copyright: © 2011-2019 Red Hat, Inc.
+Copyright: © 2011-2021 Red Hat, Inc.
 License: LGPL-2.1+
 
 Files: lib/base64.c
@@ -53,13 +53,13 @@ Copyright: © 1999-2001, 2004-2006, 2009-2018 Free Software Foundation, Inc.
 License: GPL-2+
 
 Files: lib/crypto_backend/* lib/loopaes/* lib/tcrypt/* lib/verity/*
-Copyright: © 2009-2019 Red Hat, Inc.
-           © 2010-2019 Milan Broz <gmazyland@gmail.com>
+Copyright: © 2009-2021 Red Hat, Inc.
+           © 2010-2021 Milan Broz <gmazyland@gmail.com>
 License: LGPL-2.1+
 
 Files: lib/crypto_backend/crypto_openssl.c
-Copyright: © 2009-2019 Red Hat, Inc.
-           © 2010-2019 Milan Broz <gmazyland@gmail.com>
+Copyright: © 2009-2021 Red Hat, Inc.
+           © 2010-2021 Milan Broz <gmazyland@gmail.com>
 License: LGPL-2.1+ with OpenSSL exception
 
 Files: lib/crypto_backend/argon2/*
diff --git a/debian/doc/pandoc/encrypted-boot.md b/debian/doc/pandoc/encrypted-boot.md
index 39ca4deb..52db07f4 100644
--- a/debian/doc/pandoc/encrypted-boot.md
+++ b/debian/doc/pandoc/encrypted-boot.md
@@ -158,10 +158,11 @@ system: no need to reboot into a live CD or an initramfs shell.
 You can skip the next sub-section and go directly to [Enabling
 `cryptomount` in GRUB2].  Note that `init`(1) needs to unlock the
 `/boot` partition *again* during the boot process.  See [Avoiding the
-extra password prompt] for details and a proposed workaround.  (Only
-steps 1-3 from that section are relevant here; no need to copy the key
-file to the initramfs image since `/boot` can be unlocked and mounted
-later during the boot process.)
+extra password prompt] for details and a proposed workaround.  (You'll
+need to substitute `/` resp. `sda5` with `/boot` resp. `sda1` in that
+section, however only steps 1-3 are relevant here: no need to copy the
+key file to the initramfs image since `/boot` can be unlocked and
+mounted later during the boot process.)
 
 
 Moving `/boot` to the root file system
diff --git a/debian/gbp.conf b/debian/gbp.conf
index d9d405ee..823bf5ba 100644
--- a/debian/gbp.conf
+++ b/debian/gbp.conf
@@ -1,5 +1,7 @@
 [DEFAULT]
 debian-branch = debian/latest
 upstream-branch = upstream/latest
-upstream-tag = v%(version)s
 pristine-tar = False
+
+[import-orig]
+upstream-vcs-tag = v%(version)s
diff --git a/debian/upstream-signing-key.asc b/debian/upstream/signing-key.asc
similarity index 100%
rename from debian/upstream-signing-key.asc
rename to debian/upstream/signing-key.asc
diff --git a/debian/watch b/debian/watch
index 39eee672..161cf2c2 100644
--- a/debian/watch
+++ b/debian/watch
@@ -1,4 +1,4 @@
 version=4
-#options="decompress,pgpsigurlmangle=s/\.(?:gz|xz)$/.sign/" \
-https://www.kernel.org/pub/linux/utils/cryptsetup/v(\d[\d\.]*)/ \
-	cryptsetup-(\d[\d\.]*)\.tar\.(?:gz|xz)
+options="mode=git,pgpmode=gittag" \
+  https://gitlab.com/cryptsetup/cryptsetup.git \
+  refs/tags/v?@ANY_VERSION@
diff --git a/lib/bitlk/bitlk.c b/lib/bitlk/bitlk.c
index ea3d0c5b..9c3fba37 100644
--- a/lib/bitlk/bitlk.c
+++ b/lib/bitlk/bitlk.c
@@ -54,6 +54,9 @@
 #define BITLK_RECOVERY_PARTS 8
 #define BITLK_RECOVERY_PART_LEN 6
 
+#define BITLK_BEK_FILE_HEADER_LEN 48
+#define BITLK_STARTUP_KEY_HEADER_LEN 24
+
 #define BITLK_KDF_HASH "sha256"
 #define BITLK_KDF_ITERATION_COUNT 0x100000
 
@@ -162,6 +165,18 @@ struct bitlk_kdf_data {
 	uint64_t count;
 };
 
+struct bitlk_bek_header {
+	uint32_t metadata_size;
+	uint32_t metadata_version;
+	uint32_t metadata_header_size;
+	uint32_t metada_size_copy;
+	struct bitlk_guid guid;
+	uint32_t next_nonce;
+	uint16_t encryption;
+	uint16_t unknown;
+	uint64_t creation_time;
+} __attribute__ ((packed));
+
 static BITLKVMKProtection get_vmk_protection(uint16_t protection)
 {
 	switch (protection) {
@@ -311,7 +326,9 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
 	bool supported = false;
 
 	/* only passphrase or recovery passphrase vmks are supported (can be used to activate) */
-	supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE || (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE;
+	supported = (*vmk)->protection == BITLK_PROTECTION_PASSPHRASE ||
+		    (*vmk)->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE ||
+		    (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY;
 
 	while (end - start > 2) {
 		/* size of this entry */
@@ -394,6 +411,9 @@ static int parse_vmk_entry(struct crypt_device *cd, uint8_t *data, int start, in
 				(*vmk)->name = string;
 				string = NULL;
 			}
+		/* no idea what this is, lets hope it's not important */
+		} else if (key_entry_value == BITLK_ENTRY_VALUE_USE_KEY && (*vmk)->protection == BITLK_PROTECTION_STARTUP_KEY) {
+			;
 		} else {
 			if (supported) {
 				log_err(cd, _("Unexpected metadata entry value '%u' found when parsing supported Volume Master Key."), key_entry_value);
@@ -436,6 +456,9 @@ void BITLK_bitlk_vmk_free(struct bitlk_vmk *vmk)
 
 void BITLK_bitlk_metadata_free(struct bitlk_metadata *metadata)
 {
+	if (!metadata)
+		return;
+
 	free(metadata->guid);
 	if (metadata->description)
 		free(metadata->description);
@@ -481,18 +504,6 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 		goto out;
 	}
 
-	if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) {
-		log_err(cd, _("BITLK version 1 is currently not supported."));
-		r = -ENOTSUP;
-		goto out;
-	} else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0)
-		;
-	else {
-		log_err(cd, _("Invalid or unknown boot signature for BITLK device."));
-		r = -EINVAL;
-		goto out;
-	}
-
 	if (memcmp(sig.signature, BITLK_SIGNATURE, sizeof(sig.signature)) == 0) {
 		params->togo = false;
 		fve_offset = BITLK_HEADER_METADATA_OFFSET;
@@ -505,6 +516,18 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 		goto out;
 	}
 
+	if (memcmp(sig.boot_code, BITLK_BOOTCODE_V1, sizeof(sig.boot_code)) == 0) {
+		log_err(cd, _("BITLK version 1 is currently not supported."));
+		r = -ENOTSUP;
+		goto out;
+	} else if (memcmp(sig.boot_code, BITLK_BOOTCODE_V2, sizeof(sig.boot_code)) == 0)
+		;
+	else {
+		log_err(cd, _("Invalid or unknown boot signature for BITLK device."));
+		r = -EINVAL;
+		goto out;
+	}
+
 	params->sector_size = le16_to_cpu(sig.sector_size);
 	if (params->sector_size == 0) {
 		log_dbg(cd, "Got sector size 0, assuming 512.");
@@ -564,12 +587,12 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 	switch (le16_to_cpu(fve.encryption)) {
 	/* AES-CBC with Elephant difuser */
 	case 0x8000:
-		params->key_size = 128;
+		params->key_size = 256;
 		params->cipher = "aes";
 		params->cipher_mode = "cbc-elephant";
 		break;
 	case 0x8001:
-		params->key_size = 256;
+		params->key_size = 512;
 		params->cipher = "aes";
 		params->cipher_mode = "cbc-elephant";
 		break;
@@ -586,12 +609,12 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 		break;
 	/* AES-XTS */
 	case 0x8004:
-		params->key_size = 128;
+		params->key_size = 256;
 		params->cipher = "aes";
 		params->cipher_mode = "xts-plain64";
 		break;
 	case 0x8005:
-		params->key_size = 256;
+		params->key_size = 512;
 		params->cipher = "aes";
 		params->cipher_mode = "xts-plain64";
 		break;
@@ -628,7 +651,7 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 
 	if (read_lseek_blockwise(devfd, device_block_size(cd, device),
 		device_alignment(device), fve_entries, fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN,
-		params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN) {
+		params->metadata_offset[0] + BITLK_FVE_METADATA_HEADERS_LEN) != (ssize_t)(fve_metadata_size - BITLK_FVE_METADATA_HEADER_LEN)) {
 		log_err(cd, _("Failed to read BITLK metadata entries from %s."), device_path(device));
 		r = -EINVAL;
 		goto out;
@@ -654,6 +677,10 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 			       sizeof(entry_vmk));
 
 			vmk = malloc(sizeof(struct bitlk_vmk));
+			if (!vmk) {
+				r = -ENOMEM;
+				goto out;
+			}
 			memset(vmk, 0, sizeof(struct bitlk_vmk));
 
 			guid_to_string(&entry_vmk.guid, guid_buf);
@@ -682,6 +709,10 @@ int BITLK_read_sb(struct crypt_device *cd, struct bitlk_metadata *params)
 		/* FVEK */
 		} else if (entry_type == BITLK_ENTRY_TYPE_FVEK) {
 			params->fvek = malloc(sizeof(struct bitlk_fvek));
+			if (!params->fvek) {
+				r = -ENOMEM;
+				goto out;
+			}
 			memcpy(params->fvek->nonce,
 			       fve_entries + start + BITLK_ENTRY_HEADER_LEN,
 			       sizeof(params->fvek->nonce));
@@ -838,6 +869,120 @@ static int get_recovery_key(struct crypt_device *cd,
 	return 0;
 }
 
+static int parse_external_key_entry(struct crypt_device *cd, const char *data, int start, int end, struct volume_key **vk)
+{
+	uint16_t key_entry_size = 0;
+	uint16_t key_entry_type = 0;
+	uint16_t key_entry_value = 0;
+	size_t key_size = 0;
+	const char *key = NULL;
+
+	while (end - start > 2) {
+		/* size of this entry */
+		memcpy(&key_entry_size, data + start, sizeof(key_entry_size));
+		key_entry_size = le16_to_cpu(key_entry_size);
+		if (key_entry_size == 0)
+			break;
+
+		/* type and value of this entry */
+		memcpy(&key_entry_type, data + start + sizeof(key_entry_size), sizeof(key_entry_type));
+		memcpy(&key_entry_value,
+		       data + start + sizeof(key_entry_size) + sizeof(key_entry_type),
+		       sizeof(key_entry_value));
+		key_entry_type = le16_to_cpu(key_entry_type);
+		key_entry_value = le16_to_cpu(key_entry_value);
+
+		/* only properties should be in this entry */
+		if (key_entry_type != BITLK_ENTRY_TYPE_PROPERTY) {
+			log_err(cd, _("Unexpected metadata entry type '%u' found when parsing external key."), key_entry_type);
+			return -EINVAL;
+		}
+
+		if (key_entry_value == BITLK_ENTRY_VALUE_KEY) {
+			key_size = key_entry_size - (BITLK_ENTRY_HEADER_LEN + 4);
+			key = (const char *) data + start + BITLK_ENTRY_HEADER_LEN + 4;
+			*vk = crypt_alloc_volume_key(key_size, key);
+			if (*vk == NULL)
+				return -ENOMEM;
+			return 0;
+		/* optional "ExternalKey" string, we can safely ignore it */
+		} else if (key_entry_value == BITLK_ENTRY_VALUE_STRING)
+			;
+		else {
+			log_err(cd, _("Unexpected metadata entry value '%u' found when parsing external key."), key_entry_value);
+			return -EINVAL;
+		}
+
+		start += key_entry_size;
+	}
+
+	/* if we got here we failed to parse the metadata */
+	return -EINVAL;
+}
+
+/* check if given passphrase can be a startup key (has right format) and convert it */
+static int get_startup_key(struct crypt_device *cd,
+			   const char *password,
+			   size_t passwordLen,
+			   const struct bitlk_vmk *vmk,
+			   struct volume_key **su_key)
+{
+	struct bitlk_bek_header bek_header = {0};
+	char guid_buf[UUID_STR_LEN] = {0};
+
+	uint16_t key_entry_size = 0;
+	uint16_t key_entry_type = 0;
+	uint16_t key_entry_value = 0;
+
+	if (passwordLen < BITLK_BEK_FILE_HEADER_LEN)
+		return -EPERM;
+
+	memcpy(&bek_header, password, BITLK_BEK_FILE_HEADER_LEN);
+
+	/* metadata should contain GUID of the VMK this startup key is used for */
+	guid_to_string(&bek_header.guid, guid_buf);
+	if (strcmp(guid_buf, vmk->guid) == 0)
+		log_dbg(cd, "Found matching startup key for VMK %s", vmk->guid);
+	else
+		return -EPERM;
+
+	if (bek_header.metadata_version != 1) {
+		log_err(cd, "Unsupported BEK metadata version %" PRIu32 "", bek_header.metadata_version);
+		return -ENOTSUP;
+	}
+
+	if (bek_header.metadata_size != passwordLen) {
+		log_err(cd, "Unexpected BEK metadata size  %" PRIu32 " does not match BEK file length", bek_header.metadata_size);
+		return -EINVAL;
+	}
+
+	/* we are expecting exactly one metadata entry starting immediately after the header */
+	memcpy(&key_entry_size, password + BITLK_BEK_FILE_HEADER_LEN, sizeof(key_entry_size));
+	key_entry_size = le16_to_cpu(key_entry_size);
+	if (key_entry_size < BITLK_ENTRY_HEADER_LEN) {
+		log_dbg(cd, "Unexpected metadata entry size %" PRIu16 " when parsing BEK file", key_entry_size);
+		return -EINVAL;
+	}
+
+	/* type and value of this entry */
+	memcpy(&key_entry_type, password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size), sizeof(key_entry_type));
+	memcpy(&key_entry_value,
+	       password + BITLK_BEK_FILE_HEADER_LEN + sizeof(key_entry_size) + sizeof(key_entry_type),
+	       sizeof(key_entry_value));
+	key_entry_type = le16_to_cpu(key_entry_type);
+	key_entry_value = le16_to_cpu(key_entry_value);
+
+	if (key_entry_type == BITLK_ENTRY_TYPE_STARTUP_KEY && key_entry_value == BITLK_ENTRY_VALUE_EXTERNAL_KEY) {
+		return parse_external_key_entry(cd, password,
+						BITLK_BEK_FILE_HEADER_LEN + BITLK_ENTRY_HEADER_LEN + BITLK_STARTUP_KEY_HEADER_LEN,
+						passwordLen, su_key);
+	} else {
+		log_err(cd, _("Unexpected metadata entry found when parsing startup key."));
+		log_dbg(cd, "Entry type: %u, entry value: %u", key_entry_type, key_entry_value);
+		return -EINVAL;
+	}
+}
+
 static int bitlk_kdf(struct crypt_device *cd,
 		     const char *password,
 		     size_t passwordLen,
@@ -939,7 +1084,7 @@ static int decrypt_key(struct crypt_device *cd,
 	}
 
 	if (is_fvek && strcmp(crypt_get_cipher_mode(cd), "cbc-elephant") == 0 &&
-		crypt_get_volume_key_size(cd) == 16) {
+		crypt_get_volume_key_size(cd) == 32) {
 		/* 128bit AES-CBC with Elephant -- key size is 256 bit (2 keys) but key data is 512 bits,
 		   data: 16B CBC key, 16B empty, 16B elephant key, 16B empty */
 		memcpy(outbuf + 16 + BITLK_OPEN_KEY_METADATA_LEN,
@@ -1000,12 +1145,18 @@ int BITLK_activate(struct crypt_device *cd,
 	while (next_vmk) {
 		if (next_vmk->protection == BITLK_PROTECTION_PASSPHRASE) {
 			r = bitlk_kdf(cd, password, passwordLen, false, next_vmk->salt, &vmk_dec_key);
-			if (r)
-				return r;
+			if (r) {
+				/* something wrong happened, but we still want to check other key slots */
+				next_vmk = next_vmk->next;
+				continue;
+			}
 		} else if (next_vmk->protection == BITLK_PROTECTION_RECOVERY_PASSPHRASE) {
 			r = get_recovery_key(cd, password, passwordLen, &recovery_key);
-			if (r)
-				return r;
+			if (r) {
+				/* something wrong happened, but we still want to check other key slots */
+				next_vmk = next_vmk->next;
+				continue;
+			}
 			if (recovery_key == NULL) {
 				/* r = 0 but no key -> given passphrase is not a recovery passphrase */
 				r = -EPERM;
@@ -1018,8 +1169,15 @@ int BITLK_activate(struct crypt_device *cd,
 			crypt_free_volume_key(recovery_key);
 			if (r)
 				return r;
+		} else if (next_vmk->protection == BITLK_PROTECTION_STARTUP_KEY) {
+			r = get_startup_key(cd, password, passwordLen, next_vmk, &vmk_dec_key);
+			if (r) {
+				next_vmk = next_vmk->next;
+				continue;
+			}
+			log_dbg(cd, "Trying to use external key found in provided password.");
 		} else {
-			/* only passphrase and recovery passphrase VMKs supported right now */
+			/* only passphrase, recovery passphrase and startup key VMKs supported right now */
 			log_dbg(cd, "Skipping %s", get_vmk_protection_string(next_vmk->protection));
 			next_vmk = next_vmk->next;
 			if (r == 0)
diff --git a/lib/crypto_backend/crypto_cipher_kernel.c b/lib/crypto_backend/crypto_cipher_kernel.c
index 1a8aecfc..b2a92b51 100644
--- a/lib/crypto_backend/crypto_cipher_kernel.c
+++ b/lib/crypto_backend/crypto_cipher_kernel.c
@@ -152,6 +152,9 @@ static int _crypt_cipher_crypt(struct crypt_cipher_kernel *ctx,
 	/* Set IV */
 	if (iv) {
 		header = CMSG_NXTHDR(&msg, header);
+		if (!header)
+			return -EINVAL;
+
 		header->cmsg_level = SOL_ALG;
 		header->cmsg_type = ALG_SET_IV;
 		header->cmsg_len = iv_msg_size;
diff --git a/lib/crypto_backend/pbkdf_check.c b/lib/crypto_backend/pbkdf_check.c
index 7444e0aa..2c86036c 100644
--- a/lib/crypto_backend/pbkdf_check.c
+++ b/lib/crypto_backend/pbkdf_check.c
@@ -361,8 +361,10 @@ static int crypt_pbkdf_check(const char *kdf, const char *hash,
 		ms = time_ms(&rstart, &rend);
 		if (ms) {
 			PBKDF2_temp = (double)iterations * target_ms / ms;
-			if (PBKDF2_temp > UINT32_MAX)
-				return -EINVAL;
+			if (PBKDF2_temp > UINT32_MAX) {
+				r = -EINVAL;
+				goto out;
+			}
 			*iter_secs = (uint32_t)PBKDF2_temp;
 		}
 
diff --git a/lib/integrity/integrity.c b/lib/integrity/integrity.c
index 86305ce4..39b6a39f 100644
--- a/lib/integrity/integrity.c
+++ b/lib/integrity/integrity.c
@@ -40,7 +40,7 @@ static int INTEGRITY_read_superblock(struct crypt_device *cd,
 	if (read_lseek_blockwise(devfd, device_block_size(cd, device),
 		device_alignment(device), sb, sizeof(*sb), offset) != sizeof(*sb) ||
 	    memcmp(sb->magic, SB_MAGIC, sizeof(sb->magic)) ||
-	    sb->version < SB_VERSION_1 || sb->version > SB_VERSION_4) {
+	    sb->version < SB_VERSION_1 || sb->version > SB_VERSION_5) {
 		log_std(cd, "No integrity superblock detected on %s.\n",
 			device_path(device));
 		r = -EINVAL;
@@ -92,14 +92,15 @@ int INTEGRITY_dump(struct crypt_device *cd, struct device *device, uint64_t offs
 	log_std(cd, "journal_sections %u\n", sb.journal_sections);
 	log_std(cd, "provided_data_sectors %" PRIu64 "\n", sb.provided_data_sectors);
 	log_std(cd, "sector_size %u\n", SECTOR_SIZE << sb.log2_sectors_per_block);
-	if (sb.version == SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING))
+	if (sb.version >= SB_VERSION_2 && (sb.flags & SB_FLAG_RECALCULATING))
 		log_std(cd, "recalc_sector %" PRIu64 "\n", sb.recalc_sector);
 	log_std(cd, "log2_blocks_per_bitmap %u\n", sb.log2_blocks_per_bitmap_bit);
-	log_std(cd, "flags %s%s%s%s\n",
+	log_std(cd, "flags %s%s%s%s%s\n",
 		sb.flags & SB_FLAG_HAVE_JOURNAL_MAC ? "have_journal_mac " : "",
 		sb.flags & SB_FLAG_RECALCULATING ? "recalculating " : "",
 		sb.flags & SB_FLAG_DIRTY_BITMAP ? "dirty_bitmap " : "",
-		sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "");
+		sb.flags & SB_FLAG_FIXED_PADDING ? "fix_padding " : "",
+		sb.flags & SB_FLAG_FIXED_HMAC ? "fix_hmac " : "");
 
 	return 0;
 }
@@ -278,6 +279,15 @@ int INTEGRITY_activate_dmd_device(struct crypt_device *cd,
 		return -ENOTSUP;
 	}
 
+	if (r < 0 && (dmd->flags & CRYPT_ACTIVATE_RECALCULATE) &&
+	    !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC) &&
+	    ((sb_flags & SB_FLAG_FIXED_HMAC) ?
+	    (tgt->u.integrity.vk && !tgt->u.integrity.journal_integrity_key) :
+	    (tgt->u.integrity.vk || tgt->u.integrity.journal_integrity_key))) {
+		log_err(cd, _("Kernel refuses to activate insecure recalculate option (see legacy activation options to override)."));
+		return -ENOTSUP;
+	}
+
 	return r;
 }
 
diff --git a/lib/integrity/integrity.h b/lib/integrity/integrity.h
index 38c4c5ee..72737dd6 100644
--- a/lib/integrity/integrity.h
+++ b/lib/integrity/integrity.h
@@ -35,11 +35,13 @@ struct crypt_dm_active_device;
 #define SB_VERSION_2	2
 #define SB_VERSION_3	3
 #define SB_VERSION_4	4
+#define SB_VERSION_5	5
 
 #define SB_FLAG_HAVE_JOURNAL_MAC	(1 << 0)
 #define SB_FLAG_RECALCULATING		(1 << 1) /* V2 only */
 #define SB_FLAG_DIRTY_BITMAP		(1 << 2) /* V3 only */
 #define SB_FLAG_FIXED_PADDING		(1 << 3) /* V4 only */
+#define SB_FLAG_FIXED_HMAC		(1 << 4) /* V5 only */
 
 struct superblock {
 	uint8_t magic[8];
diff --git a/lib/internal.h b/lib/internal.h
index a418a465..89403cf2 100644
--- a/lib/internal.h
+++ b/lib/internal.h
@@ -266,4 +266,12 @@ int crypt_compare_dm_devices(struct crypt_device *cd,
 			       const struct crypt_dm_active_device *tgt);
 static inline void *crypt_zalloc(size_t size) { return calloc(1, size); }
 
+static inline bool uint64_mult_overflow(uint64_t *u, uint64_t b, size_t size)
+{
+	*u = (uint64_t)b * size;
+	if ((uint64_t)(*u / size) != b)
+		return true;
+	return false;
+}
+
 #endif /* INTERNAL_H */
diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h
index 3d002c0c..9620ebbe 100644
--- a/lib/libcryptsetup.h
+++ b/lib/libcryptsetup.h
@@ -652,6 +652,10 @@ uint32_t crypt_get_compatibility(struct crypt_device *cd);
 
 /** dm-integrity device uses less effective (legacy) padding (old kernels) */
 #define CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING (1 << 0)
+/** dm-integrity device does not protect superblock with HMAC (old kernels) */
+#define CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC (1 << 1)
+/** dm-integrity allow recalculating of volumes with HMAC keys (old kernels) */
+#define CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC (1 << 2)
 
 /**
  * Convert to new type for already existing device.
@@ -1485,11 +1489,11 @@ const char *crypt_get_cipher_mode(struct crypt_device *cd);
 const char *crypt_get_uuid(struct crypt_device *cd);
 
 /**
- * Get path to underlaying device.
+ * Get path to underlying device.
  *
  * @param cd crypt device handle
  *
- * @return path to underlaying device name
+ * @return path to underlying device name
  *
  */
 const char *crypt_get_device_name(struct crypt_device *cd);
@@ -1499,7 +1503,7 @@ const char *crypt_get_device_name(struct crypt_device *cd);
  *
  * @param cd crypt device handle
  *
- * @return path to underlaying device name
+ * @return path to underlying device name
  *
  */
 const char *crypt_get_metadata_device_name(struct crypt_device *cd);
@@ -2295,7 +2299,7 @@ int crypt_reencrypt_init_by_keyring(struct crypt_device *cd,
  * Run data reencryption.
  *
  * @param cd crypt device handle
- * @param progress is a callback funtion reporting device \b size,
+ * @param progress is a callback function reporting device \b size,
  * current \b offset of reencryption and provided \b usrptr identification
  *
  * @return @e 0 on success or negative errno value otherwise.
@@ -2336,17 +2340,15 @@ crypt_reencrypt_info crypt_reencrypt_status(struct crypt_device *cd,
  *
  * @param size size of memory in bytes
  *
- * @return pointer to allocate memory or @e NULL.
+ * @return pointer to allocated memory or @e NULL.
  */
 void *crypt_safe_alloc(size_t size);
 
 /**
- * Release safe memory, content is safely wiped
+ * Release safe memory, content is safely wiped.
  * The pointer must be allocated with @link crypt_safe_alloc @endlink
  *
  * @param data pointer to memory to be deallocated
- *
- * @return pointer to allocate memory or @e NULL.
  */
 void crypt_safe_free(void *data);
 
@@ -2356,17 +2358,15 @@ void crypt_safe_free(void *data);
  * @param data pointer to memory to be deallocated
  * @param size new size of memory in bytes
  *
- * @return pointer to allocate memory or @e NULL.
+ * @return pointer to allocated memory or @e NULL.
  */
 void *crypt_safe_realloc(void *data, size_t size);
 
 /**
  * Safe clear memory area (compile should not compile this call out).
  *
- * @param data pointer to memory to cleared
- * @param size new size of memory in bytes
- *
- * @return pointer to allocate memory or @e NULL.
+ * @param data pointer to memory to be cleared
+ * @param size size of memory in bytes
  */
 void crypt_safe_memzero(void *data, size_t size);
 
diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c
index 8f8c94b5..aebd4513 100644
--- a/lib/libdevmapper.c
+++ b/lib/libdevmapper.c
@@ -239,6 +239,9 @@ static void _dm_set_integrity_compat(struct crypt_device *cd,
 	if (_dm_satisfies_version(1, 6, 0, integrity_maj, integrity_min, integrity_patch))
 		_dm_flags |= DM_INTEGRITY_DISCARDS_SUPPORTED;
 
+	if (_dm_satisfies_version(1, 7, 0, integrity_maj, integrity_min, integrity_patch))
+		_dm_flags |= DM_INTEGRITY_FIX_HMAC_SUPPORTED;
+
 	_dm_integrity_checked = true;
 }
 
@@ -648,14 +651,16 @@ static char *get_dm_crypt_params(const struct dm_target *tgt, uint32_t flags)
 	} else
 		*features = '\0';
 
-	if (!strncmp(cipher_dm, "cipher_null-", 12))
+	if (crypt_is_cipher_null(cipher_dm))
 		null_cipher = 1;
 
-	if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
+	if (null_cipher)
+		hexkey = crypt_safe_alloc(2);
+	else if (flags & CRYPT_ACTIVATE_KEYRING_KEY) {
 		keystr_len = strlen(tgt->u.crypt.vk->key_description) + int_log10(tgt->u.crypt.vk->keylength) + 10;
 		hexkey = crypt_safe_alloc(keystr_len);
 	} else
-		hexkey = crypt_safe_alloc(null_cipher ? 2 : (tgt->u.crypt.vk->keylength * 2 + 1));
+		hexkey = crypt_safe_alloc(tgt->u.crypt.vk->keylength * 2 + 1);
 
 	if (!hexkey)
 		return NULL;
@@ -728,7 +733,7 @@ static char *get_dm_verity_params(const struct dm_target *tgt, uint32_t flags)
 		snprintf(fec_features, sizeof(fec_features)-1,
 			 " use_fec_from_device %s fec_start %" PRIu64 " fec_blocks %" PRIu64 " fec_roots %" PRIu32,
 			 device_block_path(tgt->u.verity.fec_device), tgt->u.verity.fec_offset,
-			 vp->data_size + tgt->u.verity.hash_blocks, vp->fec_roots);
+			 tgt->u.verity.fec_blocks, vp->fec_roots);
 	} else
 		*fec_features = '\0';
 
@@ -912,12 +917,25 @@ static char *get_dm_integrity_params(const struct dm_target *tgt, uint32_t flags
 		strncat(features, feature, sizeof(features) - strlen(features) - 1);
 		crypt_safe_free(hexkey);
 	}
+
 	if (tgt->u.integrity.fix_padding) {
 		num_options++;
 		snprintf(feature, sizeof(feature), "fix_padding ");
 		strncat(features, feature, sizeof(features) - strlen(features) - 1);
 	}
 
+	if (tgt->u.integrity.fix_hmac) {
+		num_options++;
+		snprintf(feature, sizeof(feature), "fix_hmac ");
+		strncat(features, feature, sizeof(features) - strlen(features) - 1);
+	}
+
+	if (tgt->u.integrity.legacy_recalc) {
+		num_options++;
+		snprintf(feature, sizeof(feature), "legacy_recalculate ");
+		strncat(features, feature, sizeof(features) - strlen(features) - 1);
+	}
+
 	if (flags & CRYPT_ACTIVATE_RECALCULATE) {
 		num_options++;
 		snprintf(feature, sizeof(feature), "recalculate ");
@@ -1654,6 +1672,14 @@ int dm_create_device(struct crypt_device *cd, const char *name,
 		r = _dm_create_device(cd, name, type, dmd->uuid, dmd);
 	}
 
+	/*
+	 * Print warning if activating dm-crypt cipher_null device unless it's reencryption helper or
+	 * keyslot encryption helper device (LUKS1 cipher_null devices).
+	 */
+	if (!r && !(dmd->flags & CRYPT_ACTIVATE_PRIVATE) && single_segment(dmd) && dmd->segment.type == DM_CRYPT &&
+	    crypt_is_cipher_null(dmd->segment.u.crypt.cipher))
+		log_dbg(cd, "Activated dm-crypt device with cipher_null. Device is not encrypted.");
+
 	if (r == -EINVAL &&
 	    dmd->flags & (CRYPT_ACTIVATE_SAME_CPU_CRYPT|CRYPT_ACTIVATE_SUBMIT_FROM_CRYPT_CPUS) &&
 	    !(dmt_flags & (DM_SAME_CPU_CRYPT_SUPPORTED|DM_SUBMIT_FROM_CRYPT_CPUS_SUPPORTED)))
@@ -2474,6 +2500,10 @@ static int _dm_target_query_integrity(struct crypt_device *cd,
 				*act_flags |= CRYPT_ACTIVATE_RECALCULATE;
 			} else if (!strcmp(arg, "fix_padding")) {
 				tgt->u.integrity.fix_padding = true;
+			} else if (!strcmp(arg, "fix_hmac")) {
+				tgt->u.integrity.fix_hmac = true;
+			} else if (!strcmp(arg, "legacy_recalculate")) {
+				tgt->u.integrity.legacy_recalc = true;
 			} else if (!strcmp(arg, "allow_discards")) {
 				*act_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
 			} else /* unknown option */
@@ -2913,7 +2943,9 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
 	if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED))
 		goto out;
 
-	if (vk->key_description)
+	if (!vk->keylength)
+		msg_size = 11; // key set -
+	else if (vk->key_description)
 		msg_size = strlen(vk->key_description) + int_log10(vk->keylength) + 18;
 	else
 		msg_size = vk->keylength * 2 + 10; // key set <key>
@@ -2925,7 +2957,9 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
 	}
 
 	strcpy(msg, "key set ");
-	if (vk->key_description)
+	if (!vk->keylength)
+		snprintf(msg + 8, msg_size - 8, "-");
+	else if (vk->key_description)
 		snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", vk->keylength, vk->key_description);
 	else
 		hex_key(&msg[8], vk->keylength, vk->key);
@@ -3000,8 +3034,8 @@ err:
 
 int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size,
 	struct device *data_device, struct device *hash_device, struct device *fec_device,
-	const char *root_hash, uint32_t root_hash_size, const char *root_hash_sig_key_desc,
-	uint64_t hash_offset_block, uint64_t hash_blocks, struct crypt_params_verity *vp)
+	const char *root_hash, uint32_t root_hash_size, const char* root_hash_sig_key_desc,
+	uint64_t hash_offset_block, uint64_t fec_blocks, struct crypt_params_verity *vp)
 {
 	if (!data_device || !hash_device || !vp)
 		return -EINVAL;
@@ -3019,7 +3053,7 @@ int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t se
 	tgt->u.verity.root_hash_sig_key_desc = root_hash_sig_key_desc;
 	tgt->u.verity.hash_offset = hash_offset_block;
 	tgt->u.verity.fec_offset = vp->fec_area_offset / vp->hash_block_size;
-	tgt->u.verity.hash_blocks = hash_blocks;
+	tgt->u.verity.fec_blocks = fec_blocks;
 	tgt->u.verity.vp = vp;
 
 	return 0;
@@ -3060,6 +3094,15 @@ int dm_integrity_target_set(struct crypt_device *cd,
 	    !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING))
 		tgt->u.integrity.fix_padding = true;
 
+	if (!dm_flags(cd, DM_INTEGRITY, &dmi_flags) &&
+	    (dmi_flags & DM_INTEGRITY_FIX_HMAC_SUPPORTED) &&
+	    !(crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC))
+		tgt->u.integrity.fix_hmac = true;
+
+	/* This flag can be backported, just try to set it always */
+	if (crypt_get_compatibility(cd) & CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC)
+		tgt->u.integrity.legacy_recalc = true;
+
 	if (ip) {
 		tgt->u.integrity.journal_size = ip->journal_size;
 		tgt->u.integrity.journal_watermark = ip->journal_watermark;
diff --git a/lib/luks1/keymanage.c b/lib/luks1/keymanage.c
index a08ff50f..4cbb5f86 100644
--- a/lib/luks1/keymanage.c
+++ b/lib/luks1/keymanage.c
@@ -375,8 +375,13 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
 		log_err(ctx, _("Non standard key size, manual repair required."));
 		return -EINVAL;
 	}
-	/* cryptsetup 1.0 did not align to 4k, cannot repair this one */
-	if (LUKS_keyslots_offset(phdr) < (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
+
+	/*
+	 * cryptsetup 1.0 did not align keyslots to 4k, cannot repair this one
+	 * Also we cannot trust possibly broken keyslots metadata here through LUKS_keyslots_offset().
+	 * Expect first keyslot is aligned, if not, then manual repair is neccessary.
+	 */
+	if (phdr->keyblock[0].keyMaterialOffset < (LUKS_ALIGN_KEYSLOTS / SECTOR_SIZE)) {
 		log_err(ctx, _("Non standard keyslots alignment, manual repair required."));
 		return -EINVAL;
 	}
@@ -386,6 +391,8 @@ static int _keyslot_repair(struct luks_phdr *phdr, struct crypt_device *ctx)
 		return -EINVAL;
 
 	vk = crypt_alloc_volume_key(phdr->keyBytes, NULL);
+	if (!vk)
+		return -ENOMEM;
 
 	log_verbose(ctx, _("Repairing keyslots."));
 
@@ -955,12 +962,12 @@ static int LUKS_open_key(unsigned int keyIndex,
 		  const char *password,
 		  size_t passwordLen,
 		  struct luks_phdr *hdr,
-		  struct volume_key *vk,
+		  struct volume_key **vk,
 		  struct crypt_device *ctx)
 {
 	crypt_keyslot_info ki = LUKS_keyslot_info(hdr, keyIndex);
 	struct volume_key *derived_key;
-	char *AfKey;
+	char *AfKey = NULL;
 	size_t AFEKSize;
 	int r;
 
@@ -974,8 +981,13 @@ static int LUKS_open_key(unsigned int keyIndex,
 	if (!derived_key)
 		return -ENOMEM;
 
-	assert(vk->keylength == hdr->keyBytes);
-	AFEKSize = AF_split_sectors(vk->keylength, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE;
+	*vk = crypt_alloc_volume_key(hdr->keyBytes, NULL);
+	if (!*vk) {
+		r = -ENOMEM;
+		goto out;
+	}
+
+	AFEKSize = AF_split_sectors(hdr->keyBytes, hdr->keyblock[keyIndex].stripes) * SECTOR_SIZE;
 	AfKey = crypt_safe_alloc(AFEKSize);
 	if (!AfKey) {
 		r = -ENOMEM;
@@ -1001,16 +1013,20 @@ static int LUKS_open_key(unsigned int keyIndex,
 	if (r < 0)
 		goto out;
 
-	r = AF_merge(ctx, AfKey, vk->key, vk->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec);
+	r = AF_merge(ctx, AfKey, (*vk)->key, (*vk)->keylength, hdr->keyblock[keyIndex].stripes, hdr->hashSpec);
 	if (r < 0)
 		goto out;
 
-	r = LUKS_verify_volume_key(hdr, vk);
+	r = LUKS_verify_volume_key(hdr, *vk);
 
 	/* Allow only empty passphrase with null cipher */
-	if (!r && !strcmp(hdr->cipherName, "cipher_null") && passwordLen)
+	if (!r && crypt_is_cipher_null(hdr->cipherName) && passwordLen)
 		r = -EPERM;
 out:
+	if (r < 0) {
+		crypt_free_volume_key(*vk);
+		*vk = NULL;
+	}
 	crypt_safe_free(AfKey);
 	crypt_free_volume_key(derived_key);
 	return r;
@@ -1026,16 +1042,14 @@ int LUKS_open_key_with_hdr(int keyIndex,
 	unsigned int i, tried = 0;
 	int r;
 
-	*vk = crypt_alloc_volume_key(hdr->keyBytes, NULL);
-
 	if (keyIndex >= 0) {
-		r = LUKS_open_key(keyIndex, password, passwordLen, hdr, *vk, ctx);
+		r = LUKS_open_key(keyIndex, password, passwordLen, hdr, vk, ctx);
 		return (r < 0) ? r : keyIndex;
 	}
 
 	for (i = 0; i < LUKS_NUMKEYS; i++) {
-		r = LUKS_open_key(i, password, passwordLen, hdr, *vk, ctx);
-		if(r == 0)
+		r = LUKS_open_key(i, password, passwordLen, hdr, vk, ctx);
+		if (r == 0)
 			return i;
 
 		/* Do not retry for errors that are no -EPERM or -ENOENT,
diff --git a/lib/luks2/luks2.h b/lib/luks2/luks2.h
index 5b29a627..79560d33 100644
--- a/lib/luks2/luks2.h
+++ b/lib/luks2/luks2.h
@@ -23,6 +23,8 @@
 #define _CRYPTSETUP_LUKS2_ONDISK_H
 
 #include <stdbool.h>
+#include <stdint.h>
+#include <sys/types.h>
 
 #include "libcryptsetup.h"
 
@@ -330,6 +332,12 @@ int LUKS2_token_is_assigned(struct crypt_device *cd,
 	int keyslot,
 	int token);
 
+int LUKS2_token_assignment_copy(struct crypt_device *cd,
+			struct luks2_hdr *hdr,
+			int keyslot_from,
+			int keyslot_to,
+			int commit);
+
 int LUKS2_token_create(struct crypt_device *cd,
 	struct luks2_hdr *hdr,
 	int token,
diff --git a/lib/luks2/luks2_disk_metadata.c b/lib/luks2/luks2_disk_metadata.c
index 9654cdb8..47ad5462 100644
--- a/lib/luks2/luks2_disk_metadata.c
+++ b/lib/luks2/luks2_disk_metadata.c
@@ -175,13 +175,13 @@ static void hdr_to_disk(struct luks2_hdr *hdr,
 	hdr_disk->hdr_offset  = cpu_to_be64(offset);
 	hdr_disk->seqid       = cpu_to_be64(hdr->seqid);
 
-	strncpy(hdr_disk->label, hdr->label, LUKS2_LABEL_L);
+	memcpy(hdr_disk->label, hdr->label, MIN(strlen(hdr->label), LUKS2_LABEL_L));
 	hdr_disk->label[LUKS2_LABEL_L - 1] = '\0';
-	strncpy(hdr_disk->subsystem, hdr->subsystem, LUKS2_LABEL_L);
+	memcpy(hdr_disk->subsystem, hdr->subsystem, MIN(strlen(hdr->subsystem), LUKS2_LABEL_L));
 	hdr_disk->subsystem[LUKS2_LABEL_L - 1] = '\0';
-	strncpy(hdr_disk->checksum_alg, hdr->checksum_alg, LUKS2_CHECKSUM_ALG_L);
+	memcpy(hdr_disk->checksum_alg, hdr->checksum_alg, MIN(strlen(hdr->checksum_alg), LUKS2_CHECKSUM_ALG_L));
 	hdr_disk->checksum_alg[LUKS2_CHECKSUM_ALG_L - 1] = '\0';
-	strncpy(hdr_disk->uuid, hdr->uuid, LUKS2_UUID_L);
+	memcpy(hdr_disk->uuid, hdr->uuid, MIN(strlen(hdr->uuid), LUKS2_UUID_L));
 	hdr_disk->uuid[LUKS2_UUID_L - 1] = '\0';
 
 	memcpy(hdr_disk->salt, secondary ? hdr->salt2 : hdr->salt1, LUKS2_SALT_L);
diff --git a/lib/luks2/luks2_json_format.c b/lib/luks2/luks2_json_format.c
index fb695f08..5cc92d98 100644
--- a/lib/luks2/luks2_json_format.c
+++ b/lib/luks2/luks2_json_format.c
@@ -244,7 +244,8 @@ int LUKS2_generate_hdr(
 		/* Decrease keyslots_size due to metadata device being too small */
 		if (!device_size(crypt_metadata_device(cd), &mdev_size) &&
 		    ((keyslots_size + get_min_offset(hdr)) > mdev_size) &&
-		    device_fallocate(crypt_metadata_device(cd), keyslots_size + get_min_offset(hdr)))
+		    device_fallocate(crypt_metadata_device(cd), keyslots_size + get_min_offset(hdr)) &&
+		    (get_min_offset(hdr) <= mdev_size))
 			keyslots_size = mdev_size - get_min_offset(hdr);
 	}
 
diff --git a/lib/luks2/luks2_keyslot.c b/lib/luks2/luks2_keyslot.c
index 3b8c889d..c90cb728 100644
--- a/lib/luks2/luks2_keyslot.c
+++ b/lib/luks2/luks2_keyslot.c
@@ -148,7 +148,7 @@ int LUKS2_keyslot_cipher_incompatible(struct crypt_device *cd, const char *ciphe
 {
 	char cipher[MAX_CIPHER_LEN], cipher_mode[MAX_CIPHER_LEN];
 
-	if (!cipher_spec || !strcmp(cipher_spec, "null") || !strcmp(cipher_spec, "cipher_null"))
+	if (!cipher_spec || crypt_is_cipher_null(cipher_spec))
 		return 1;
 
 	if (crypt_parse_name_and_mode(cipher_spec, cipher, NULL, cipher_mode) < 0)
diff --git a/lib/luks2/luks2_reencrypt.c b/lib/luks2/luks2_reencrypt.c
index a76cb16e..ed914a27 100644
--- a/lib/luks2/luks2_reencrypt.c
+++ b/lib/luks2/luks2_reencrypt.c
@@ -2238,7 +2238,7 @@ static int reencrypt_verify_and_upload_keys(struct crypt_device *cd, struct luks
 			if (LUKS2_digest_verify_by_digest(cd, hdr, digest_new, vk) != digest_new)
 				return -EINVAL;
 
-			if (crypt_use_keyring_for_vk(cd) &&
+			if (crypt_use_keyring_for_vk(cd) && !crypt_is_cipher_null(reencrypt_segment_cipher_new(hdr)) &&
 			    (r = LUKS2_volume_key_load_in_keyring_by_digest(cd, hdr, vk, crypt_volume_key_get_id(vk))))
 				return r;
 		}
@@ -2254,7 +2254,7 @@ static int reencrypt_verify_and_upload_keys(struct crypt_device *cd, struct luks
 				r = -EINVAL;
 				goto err;
 			}
-			if (crypt_use_keyring_for_vk(cd) &&
+			if (crypt_use_keyring_for_vk(cd) && !crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr)) &&
 			    (r = LUKS2_volume_key_load_in_keyring_by_digest(cd, hdr, vk, crypt_volume_key_get_id(vk))))
 				goto err;
 		}
@@ -2664,6 +2664,7 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd,
 	struct luks2_hdr *hdr;
 	struct crypt_lock_handle *reencrypt_lock;
 	struct luks2_reenc_context *rh;
+	const struct volume_key *vk;
 	struct crypt_dm_active_device dmd_target, dmd_source = {
 		.uuid = crypt_get_uuid(cd),
 		.flags = CRYPT_ACTIVATE_SHARED /* turn off exclusive open checks */
@@ -2730,6 +2731,19 @@ static int reencrypt_load_by_passphrase(struct crypt_device *cd,
 			goto err;
 		flags = dmd_target.flags;
 
+		/*
+		 * By default reencryption code aims to retain flags from existing dm device.
+		 * The keyring activation flag can not be inherited if original cipher is null.
+		 *
+		 * In this case override the flag based on decision made in reencrypt_verify_and_upload_keys
+		 * above. The code checks if new VK is eligible for keyring.
+		 */
+		vk = crypt_volume_key_by_id(*vks, LUKS2_reencrypt_digest_new(hdr));
+		if (vk && vk->key_description && crypt_is_cipher_null(reencrypt_segment_cipher_old(hdr))) {
+			flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+			dmd_source.flags |= CRYPT_ACTIVATE_KEYRING_KEY;
+		}
+
 		r = LUKS2_assembly_multisegment_dmd(cd, hdr, *vks, LUKS2_get_segments_jobj(hdr), &dmd_source);
 		if (!r) {
 			r = crypt_compare_dm_devices(cd, &dmd_source, &dmd_target);
diff --git a/lib/luks2/luks2_token.c b/lib/luks2/luks2_token.c
index ad6722a3..101f4f59 100644
--- a/lib/luks2/luks2_token.c
+++ b/lib/luks2/luks2_token.c
@@ -329,7 +329,7 @@ static void LUKS2_token_buffer_free(struct crypt_device *cd,
 {
 	const crypt_token_handler *h = LUKS2_token_handler(cd, token);
 
-	if (h->buffer_free)
+	if (h && h->buffer_free)
 		h->buffer_free(buffer, buffer_len);
 	else {
 		crypt_safe_memzero(buffer, buffer_len);
@@ -383,6 +383,7 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
 		uint32_t flags,
 		void *usrptr)
 {
+	bool use_keyring;
 	int keyslot, r;
 	char *buffer;
 	size_t buffer_len;
@@ -404,7 +405,13 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
 
 	keyslot = r;
 
-	if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) && crypt_use_keyring_for_vk(cd)) {
+	if (!crypt_use_keyring_for_vk(cd))
+		use_keyring = false;
+	else
+		use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) ||
+			       (flags & CRYPT_ACTIVATE_KEYRING_KEY));
+
+	if (use_keyring) {
 		if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot)))
 			flags |= CRYPT_ACTIVATE_KEYRING_KEY;
 	}
@@ -576,16 +583,12 @@ int LUKS2_token_assign(struct crypt_device *cd, struct luks2_hdr *hdr,
 	return token;
 }
 
-int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr,
-			    int keyslot, int token)
+static int token_is_assigned(struct luks2_hdr *hdr, int keyslot, int token)
 {
 	int i;
-	json_object *jobj_token, *jobj_token_keyslots, *jobj;
+	json_object *jobj, *jobj_token_keyslots,
+		    *jobj_token = LUKS2_get_token_jobj(hdr, token);
 
-	if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX || token < 0 || token >= LUKS2_TOKENS_MAX)
-		return -EINVAL;
-
-	jobj_token = LUKS2_get_token_jobj(hdr, token);
 	if (!jobj_token)
 		return -ENOENT;
 
@@ -600,6 +603,15 @@ int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr,
 	return -ENOENT;
 }
 
+int LUKS2_token_is_assigned(struct crypt_device *cd, struct luks2_hdr *hdr,
+			    int keyslot, int token)
+{
+	if (keyslot < 0 || keyslot >= LUKS2_KEYSLOTS_MAX || token < 0 || token >= LUKS2_TOKENS_MAX)
+		return -EINVAL;
+
+	return token_is_assigned(hdr, keyslot, token);
+}
+
 int LUKS2_tokens_count(struct luks2_hdr *hdr)
 {
 	json_object *jobj_tokens = LUKS2_get_tokens_jobj(hdr);
@@ -608,3 +620,28 @@ int LUKS2_tokens_count(struct luks2_hdr *hdr)
 
 	return json_object_object_length(jobj_tokens);
 }
+
+int LUKS2_token_assignment_copy(struct crypt_device *cd,
+			struct luks2_hdr *hdr,
+			int keyslot_from,
+			int keyslot_to,
+			int commit)
+{
+	int i, r;
+
+	if (keyslot_from < 0 || keyslot_from >= LUKS2_KEYSLOTS_MAX || keyslot_to < 0 || keyslot_to >= LUKS2_KEYSLOTS_MAX)
+		return -EINVAL;
+
+	r = LUKS2_tokens_count(hdr);
+	if (r <= 0)
+		return r;
+
+	for (i = 0; i < LUKS2_TOKENS_MAX; i++) {
+		if (!token_is_assigned(hdr, keyslot_from, i)) {
+			if ((r = assign_one_token(cd, hdr, keyslot_to, i, 1)))
+				return r;
+		}
+	}
+
+	return commit ? LUKS2_hdr_write(cd, hdr) : 0;
+}
diff --git a/lib/setup.c b/lib/setup.c
index d8e665c0..1e8e456f 100644
--- a/lib/setup.c
+++ b/lib/setup.c
@@ -1210,7 +1210,7 @@ static int _init_by_name_crypt(struct crypt_device *cd, const char *name)
 	}
 
 	/* do not try to lookup LUKS2 header in detached header mode */
-	if (!cd->metadata_device && !found) {
+	if (dmd.uuid && !cd->metadata_device && !found) {
 		while (*dep && !found) {
 			r = dm_query_device(cd, *dep, DM_ACTIVE_DEVICE, &dmdep);
 			if (r < 0)
@@ -2035,7 +2035,7 @@ static int _crypt_format_verity(struct crypt_device *cd,
 	} else
 		cd->u.verity.hdr.data_size = params->data_size;
 
-	if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd)) &&
+	if (device_is_identical(crypt_metadata_device(cd), crypt_data_device(cd)) > 0 &&
 	   (cd->u.verity.hdr.data_size * params->data_block_size) > params->hash_area_offset) {
 		log_err(cd, _("Data area overlaps with hash area."));
 		return -EINVAL;
@@ -2060,14 +2060,14 @@ static int _crypt_format_verity(struct crypt_device *cd,
 		}
 
 		hash_blocks_size = VERITY_hash_blocks(cd, params) * params->hash_block_size;
-		if (device_is_identical(crypt_metadata_device(cd), fec_device) &&
+		if (device_is_identical(crypt_metadata_device(cd), fec_device) > 0 &&
 		    (params->hash_area_offset + hash_blocks_size) > params->fec_area_offset) {
 			log_err(cd, _("Hash area overlaps with FEC area."));
 			r = -EINVAL;
 			goto err;
 		}
 
-		if (device_is_identical(crypt_data_device(cd), fec_device) &&
+		if (device_is_identical(crypt_data_device(cd), fec_device) > 0 &&
 		    (cd->u.verity.hdr.data_size * params->data_block_size) > params->fec_area_offset) {
 			log_err(cd, _("Data area overlaps with FEC area."));
 			r = -EINVAL;
@@ -2388,11 +2388,6 @@ static int _compare_crypt_devices(struct crypt_device *cd,
 	if (!src->u.crypt.vk || !tgt->u.crypt.vk)
 		return -EINVAL;
 
-	if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) {
-		log_dbg(cd, "Keys in context and target device do not match.");
-		return -EINVAL;
-	}
-
 	/* CIPHER checks */
 	if (!src->u.crypt.cipher || !tgt->u.crypt.cipher)
 		return -EINVAL;
@@ -2400,6 +2395,14 @@ static int _compare_crypt_devices(struct crypt_device *cd,
 		log_dbg(cd, "Cipher specs do not match.");
 		return -EINVAL;
 	}
+
+	if (tgt->u.crypt.vk->keylength == 0 && crypt_is_cipher_null(tgt->u.crypt.cipher))
+		log_dbg(cd, "Existing device uses cipher null. Skipping key comparison.");
+	else if (_compare_volume_keys(src->u.crypt.vk, 0, tgt->u.crypt.vk, tgt->u.crypt.vk->key_description != NULL)) {
+		log_dbg(cd, "Keys in context and target device do not match.");
+		return -EINVAL;
+	}
+
 	if (crypt_strcmp(src->u.crypt.integrity, tgt->u.crypt.integrity)) {
 		log_dbg(cd, "Integrity parameters do not match.");
 		return -EINVAL;
@@ -2413,7 +2416,7 @@ static int _compare_crypt_devices(struct crypt_device *cd,
 		return -EINVAL;
 	}
 
-	if (!device_is_identical(src->data_device, tgt->data_device)) {
+	if (device_is_identical(src->data_device, tgt->data_device) <= 0) {
 		log_dbg(cd, "Data devices do not match.");
 		return -EINVAL;
 	}
@@ -2467,7 +2470,7 @@ static int _compare_integrity_devices(struct crypt_device *cd,
 		return -EINVAL;
 	}
 
-	if (!device_is_identical(src->data_device, tgt->data_device)) {
+	if (device_is_identical(src->data_device, tgt->data_device) <= 0) {
 		log_dbg(cd, "Data devices do not match.");
 		return -EINVAL;
 	}
@@ -2770,6 +2773,11 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
 	if (!cd || !cd->type || !name)
 		return -EINVAL;
 
+	if (isTCRYPT(cd->type) || isBITLK(cd->type)) {
+		log_err(cd, _("This operation is not supported for this device type."));
+		return -ENOTSUP;
+	}
+
 	log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size);
 
 	r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY, &dmdq);
@@ -3090,6 +3098,45 @@ out:
 	return r;
 }
 
+/* key must be properly verified */
+static int resume_by_volume_key(struct crypt_device *cd,
+		struct volume_key *vk,
+		const char *name)
+{
+	int digest, r;
+	struct volume_key *zerokey = NULL;
+
+	if (crypt_is_cipher_null(crypt_get_cipher_spec(cd))) {
+		zerokey = crypt_alloc_volume_key(0, NULL);
+		if (!zerokey)
+			return -ENOMEM;
+		vk = zerokey;
+	} else if (crypt_use_keyring_for_vk(cd)) {
+		/* LUKS2 path only */
+		digest = LUKS2_digest_by_segment(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
+		if (digest < 0)
+			return -EINVAL;
+		r = LUKS2_volume_key_load_in_keyring_by_digest(cd,
+					&cd->u.luks2.hdr, vk, digest);
+		if (r < 0)
+			return r;
+	}
+
+	r = dm_resume_and_reinstate_key(cd, name, vk);
+
+	if (r == -ENOTSUP)
+		log_err(cd, _("Resume is not supported for device %s."), name);
+	else if (r)
+		log_err(cd, _("Error during resuming device %s."), name);
+
+	if (r < 0)
+		crypt_drop_keyring_key(cd, vk);
+
+	crypt_free_volume_key(zerokey);
+
+	return r;
+}
+
 int crypt_resume_by_passphrase(struct crypt_device *cd,
 			       const char *name,
 			       int keyslot,
@@ -3125,32 +3172,13 @@ int crypt_resume_by_passphrase(struct crypt_device *cd,
 		r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase, passphrase_size, &vk);
 
 	if  (r < 0)
-		goto out;
+		return r;
 
 	keyslot = r;
 
-	if (crypt_use_keyring_for_vk(cd)) {
-		if (!isLUKS2(cd->type)) {
-			r = -EINVAL;
-			goto out;
-		}
-		r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
-					&cd->u.luks2.hdr, vk, keyslot);
-		if (r < 0)
-			goto out;
-	}
-
-	r = dm_resume_and_reinstate_key(cd, name, vk);
+	r = resume_by_volume_key(cd, vk, name);
 
-	if (r == -ENOTSUP)
-		log_err(cd, _("Resume is not supported for device %s."), name);
-	else if (r)
-		log_err(cd, _("Error during resuming device %s."), name);
-out:
-	if (r < 0)
-		crypt_drop_keyring_key(cd, vk);
 	crypt_free_volume_key(vk);
-
 	return r < 0 ? r : keyslot;
 }
 
@@ -3189,35 +3217,22 @@ int crypt_resume_by_keyfile_device_offset(struct crypt_device *cd,
 				      &passphrase_read, &passphrase_size_read,
 				      keyfile_offset, keyfile_size, 0);
 	if (r < 0)
-		goto out;
+		return r;
 
 	if (isLUKS1(cd->type))
 		r = LUKS_open_key_with_hdr(keyslot, passphrase_read, passphrase_size_read,
 					   &cd->u.luks1.hdr, &vk, cd);
 	else
 		r = LUKS2_keyslot_open(cd, keyslot, CRYPT_DEFAULT_SEGMENT, passphrase_read, passphrase_size_read, &vk);
+
+	crypt_safe_free(passphrase_read);
 	if (r < 0)
-		goto out;
+		return r;
+
 	keyslot = r;
 
-	if (crypt_use_keyring_for_vk(cd)) {
-		if (!isLUKS2(cd->type)) {
-			r = -EINVAL;
-			goto out;
-		}
-		r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
-					&cd->u.luks2.hdr, vk, keyslot);
-		if (r < 0)
-			goto out;
-	}
+	r = resume_by_volume_key(cd, vk, name);
 
-	r = dm_resume_and_reinstate_key(cd, name, vk);
-	if (r < 0)
-		log_err(cd, _("Error during resuming device %s."), name);
-out:
-	crypt_safe_free(passphrase_read);
-	if (r < 0)
-		crypt_drop_keyring_key(cd, vk);
 	crypt_free_volume_key(vk);
 	return r < 0 ? r : keyslot;
 }
@@ -3280,24 +3295,10 @@ int crypt_resume_by_volume_key(struct crypt_device *cd,
 		r = -EINVAL;
 	if (r == -EPERM || r == -ENOENT)
 		log_err(cd, _("Volume key does not match the volume."));
-	if  (r < 0)
-		goto out;
-	r = 0;
 
-	if (crypt_use_keyring_for_vk(cd)) {
-		r = LUKS2_key_description_by_segment(cd, &cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT);
-		if (!r)
-			r = crypt_volume_key_load_in_keyring(cd, vk);
-	}
-	if  (r < 0)
-		goto out;
+	if (r >= 0)
+		r = resume_by_volume_key(cd, vk, name);
 
-	r = dm_resume_and_reinstate_key(cd, name, vk);
-	if (r < 0)
-		log_err(cd, _("Error during resuming device %s."), name);
-out:
-	if (r < 0)
-		crypt_drop_keyring_key(cd, vk);
 	crypt_free_volume_key(vk);
 	return r;
 }
@@ -3459,6 +3460,9 @@ int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
 			r = LUKS2_digest_assign(cd, &cd->u.luks2.hdr, keyslot_new, digest, 1, 0);
 			if (r < 0)
 				goto out;
+			r = LUKS2_token_assignment_copy(cd, &cd->u.luks2.hdr, keyslot_old, keyslot_new, 0);
+			if (r < 0)
+				goto out;
 		} else {
 			log_dbg(cd, "Key slot %d is going to be overwritten.", keyslot_old);
 			/* FIXME: improve return code so that we can detect area is damaged */
@@ -3686,7 +3690,7 @@ static int _check_header_data_overlap(struct crypt_device *cd, const char *name)
 	if (!name || !isLUKS(cd->type))
 		return 0;
 
-	if (!device_is_identical(crypt_data_device(cd), crypt_metadata_device(cd)))
+	if (device_is_identical(crypt_data_device(cd), crypt_metadata_device(cd)) <= 0)
 		return 0;
 
 	/* FIXME: check real header size */
@@ -3879,6 +3883,7 @@ static int _open_and_activate(struct crypt_device *cd,
 	size_t passphrase_size,
 	uint32_t flags)
 {
+	bool use_keyring;
 	int r;
 	struct volume_key *vk = NULL;
 
@@ -3890,8 +3895,13 @@ static int _open_and_activate(struct crypt_device *cd,
 		return r;
 	keyslot = r;
 
-	if ((name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) &&
-	    crypt_use_keyring_for_vk(cd)) {
+	if (!crypt_use_keyring_for_vk(cd))
+		use_keyring = false;
+	else
+		use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) ||
+			       (flags & CRYPT_ACTIVATE_KEYRING_KEY));
+
+	if (use_keyring) {
 		r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
 				&cd->u.luks2.hdr, vk, keyslot);
 		if (r < 0)
@@ -4274,6 +4284,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
 	size_t volume_key_size,
 	uint32_t flags)
 {
+	bool use_keyring;
 	struct volume_key *vk = NULL;
 	int r;
 
@@ -4349,8 +4360,12 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
 		if (r > 0)
 			r = 0;
 
-		if (!r && (name || (flags & CRYPT_ACTIVATE_KEYRING_KEY)) &&
-		    crypt_use_keyring_for_vk(cd)) {
+		if (!crypt_use_keyring_for_vk(cd))
+			use_keyring = false;
+		else
+			use_keyring = (name && !crypt_is_cipher_null(crypt_get_cipher(cd))) || (flags & CRYPT_ACTIVATE_KEYRING_KEY);
+
+		if (!r && use_keyring) {
 			r = LUKS2_key_description_by_segment(cd,
 				&cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT);
 			if (!r)
@@ -4411,7 +4426,7 @@ int crypt_activate_by_signed_key(struct crypt_device *cd,
 		return -EINVAL;
 	}
 
-	log_dbg(cd, "%s volume %s by signed key.", name ? "Activating" : "Checking", name ?: "");
+	log_dbg(cd, "%s volume %s by %skey.", name ? "Activating" : "Checking", name ?: "", signature ? "signed " : "");
 
 	if (cd->u.verity.hdr.flags & CRYPT_VERITY_ROOT_HASH_SIGNATURE && !signature) {
 		log_err(cd, _("Root hash signature required."));
diff --git a/lib/utils_blkid.h b/lib/utils_blkid.h
index ca50da70..d03d4a50 100644
--- a/lib/utils_blkid.h
+++ b/lib/utils_blkid.h
@@ -21,6 +21,8 @@
 #ifndef _UTILS_BLKID_H
 #define _UTILS_BLKID_H
 
+#include <sys/types.h>
+
 struct blkid_handle;
 
 typedef enum { PRB_OK = 0, PRB_EMPTY, PRB_AMBIGUOUS, PRB_FAIL } blk_probe_status;
diff --git a/lib/utils_crypt.c b/lib/utils_crypt.c
index f09871c2..d799162b 100644
--- a/lib/utils_crypt.c
+++ b/lib/utils_crypt.c
@@ -177,3 +177,10 @@ ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc)
 	*result = bytes;
 	return i;
 }
+
+bool crypt_is_cipher_null(const char *cipher_spec)
+{
+	if (!cipher_spec)
+		return false;
+	return (strstr(cipher_spec, "cipher_null") || !strcmp(cipher_spec, "null"));
+}
diff --git a/lib/utils_crypt.h b/lib/utils_crypt.h
index 32b77cb8..c5aa7068 100644
--- a/lib/utils_crypt.h
+++ b/lib/utils_crypt.h
@@ -23,6 +23,7 @@
 #ifndef _UTILS_CRYPT_H
 #define _UTILS_CRYPT_H
 
+#include <stdbool.h>
 #include <unistd.h>
 
 #define MAX_CIPHER_LEN		32
@@ -38,4 +39,6 @@ int crypt_parse_pbkdf(const char *s, const char **pbkdf);
 
 ssize_t crypt_hex_to_bytes(const char *hex, char **result, int safe_alloc);
 
+bool crypt_is_cipher_null(const char *cipher_spec);
+
 #endif /* _UTILS_CRYPT_H */
diff --git a/lib/utils_device.c b/lib/utils_device.c
index ddbe8362..5dda6924 100644
--- a/lib/utils_device.c
+++ b/lib/utils_device.c
@@ -300,6 +300,9 @@ static int device_open_internal(struct crypt_device *cd, struct device *device,
 
 int device_open(struct crypt_device *cd, struct device *device, int flags)
 {
+	if (!device)
+		return -EINVAL;
+
 	assert(!device_locked(device->lh));
 	return device_open_internal(cd, device, flags);
 }
@@ -354,6 +357,9 @@ void device_release_excl(struct crypt_device *cd, struct device *device)
 
 int device_open_locked(struct crypt_device *cd, struct device *device, int flags)
 {
+	if (!device)
+		return -EINVAL;
+
 	assert(!crypt_metadata_locking_enabled() || device_locked(device->lh));
 	return device_open_internal(cd, device, flags);
 }
@@ -528,7 +534,7 @@ void device_topology_alignment(struct crypt_device *cd,
 	if ((temp_alignment < (unsigned long)opt_io_size) &&
 	    !((unsigned long)opt_io_size % temp_alignment) && !MISALIGNED_4K(opt_io_size))
 		temp_alignment = (unsigned long)opt_io_size;
-	else if (opt_io_size)
+	else if (opt_io_size && (opt_io_size != min_io_size))
 		log_err(cd, _("Ignoring bogus optimal-io size for data device (%u bytes)."), opt_io_size);
 
 	/* If calculated alignment is multiple of default, keep default */
@@ -589,8 +595,11 @@ int device_size(struct device *device, uint64_t *size)
 	struct stat st;
 	int devfd, r = -EINVAL;
 
+	if (!device)
+		return -EINVAL;
+
 	devfd = open(device->path, O_RDONLY);
-	if(devfd == -1)
+	if (devfd == -1)
 		return -EINVAL;
 
 	if (fstat(devfd, &st) < 0)
@@ -612,6 +621,9 @@ int device_fallocate(struct device *device, uint64_t size)
 	struct stat st;
 	int devfd, r = -EINVAL;
 
+	if (!device)
+		return -EINVAL;
+
 	devfd = open(device_path(device), O_RDWR);
 	if (devfd == -1)
 		return -EINVAL;
@@ -852,22 +864,30 @@ size_t size_round_up(size_t size, size_t block)
 
 void device_disable_direct_io(struct device *device)
 {
+	if (device)
 		device->o_direct = 0;
 }
 
 int device_direct_io(const struct device *device)
 {
-	return device->o_direct;
+	return device ? device->o_direct : 0;
 }
 
-static dev_t device_devno(const struct device *device)
+static int device_compare_path(const char *path1, const char *path2)
 {
-	struct stat st;
+	struct stat st_path1, st_path2;
 
-	if (stat(device->path, &st) || !S_ISBLK(st.st_mode))
-		return 0;
+	if (stat(path1, &st_path1 ) < 0 || stat(path2, &st_path2 ) < 0)
+		return -EINVAL;
+
+	if (S_ISBLK(st_path1.st_mode) && S_ISBLK(st_path2.st_mode))
+		return (st_path1.st_rdev == st_path2.st_rdev) ? 1 : 0;
+
+	if (S_ISREG(st_path1.st_mode) && S_ISREG(st_path2.st_mode))
+		return (st_path1.st_ino == st_path2.st_ino &&
+			st_path1.st_dev == st_path2.st_dev) ? 1 : 0;
 
-	return st.st_rdev;
+	return 0;
 }
 
 int device_is_identical(struct device *device1, struct device *device2)
@@ -878,21 +898,19 @@ int device_is_identical(struct device *device1, struct device *device2)
 	if (device1 == device2)
 		return 1;
 
-	if (device1->init_done && device2->init_done)
-		return (device_devno(device1) == device_devno(device2));
-	else if (device1->init_done || device2->init_done)
-		return 0;
-
 	if (!strcmp(device_path(device1), device_path(device2)))
 		return 1;
 
-	return 0;
+	return device_compare_path(device_path(device1), device_path(device2));
 }
 
 int device_is_rotational(struct device *device)
 {
 	struct stat st;
 
+	if (!device)
+		return -EINVAL;
+
 	if (stat(device_path(device), &st) < 0)
 		return -EINVAL;
 
@@ -906,6 +924,9 @@ size_t device_alignment(struct device *device)
 {
 	int devfd;
 
+	if (!device)
+		return -EINVAL;
+
 	if (!device->alignment) {
 		devfd = open(device_path(device), O_RDONLY);
 		if (devfd != -1) {
@@ -919,17 +940,18 @@ size_t device_alignment(struct device *device)
 
 void device_set_lock_handle(struct device *device, struct crypt_lock_handle *h)
 {
+	if (device)
 		device->lh = h;
 }
 
 struct crypt_lock_handle *device_get_lock_handle(struct device *device)
 {
-	return device->lh;
+	return device ? device->lh : NULL;
 }
 
 int device_read_lock(struct crypt_device *cd, struct device *device)
 {
-	if (!crypt_metadata_locking_enabled())
+	if (!device || !crypt_metadata_locking_enabled())
 		return 0;
 
 	if (device_read_lock_internal(cd, device))
@@ -940,7 +962,7 @@ int device_read_lock(struct crypt_device *cd, struct device *device)
 
 int device_write_lock(struct crypt_device *cd, struct device *device)
 {
-	if (!crypt_metadata_locking_enabled())
+	if (!device || !crypt_metadata_locking_enabled())
 		return 0;
 
 	assert(!device_locked(device->lh) || !device_locked_readonly(device->lh));
@@ -950,7 +972,7 @@ int device_write_lock(struct crypt_device *cd, struct device *device)
 
 void device_read_unlock(struct crypt_device *cd, struct device *device)
 {
-	if (!crypt_metadata_locking_enabled())
+	if (!device || !crypt_metadata_locking_enabled())
 		return;
 
 	assert(device_locked(device->lh));
@@ -960,7 +982,7 @@ void device_read_unlock(struct crypt_device *cd, struct device *device)
 
 void device_write_unlock(struct crypt_device *cd, struct device *device)
 {
-	if (!crypt_metadata_locking_enabled())
+	if (!device || !crypt_metadata_locking_enabled())
 		return;
 
 	assert(device_locked(device->lh) && !device_locked_readonly(device->lh));
diff --git a/lib/utils_device_locking.c b/lib/utils_device_locking.c
index dac8315a..c50dff8f 100644
--- a/lib/utils_device_locking.c
+++ b/lib/utils_device_locking.c
@@ -106,7 +106,7 @@ static int open_lock_dir(struct crypt_device *cd, const char *dir, const char *b
 	lockdfd = openat(dirfd, base, O_RDONLY | O_NOFOLLOW | O_DIRECTORY | O_CLOEXEC);
 	if (lockdfd < 0) {
 		if (errno == ENOENT) {
-			log_std(cd, _("WARNING: Locking directory %s/%s is missing!\n"), dir, base);
+			log_dbg(cd, _("Locking directory %s/%s will be created with default compiled-in permissions."), dir, base);
 
 			/* success or failure w/ errno == EEXIST either way just try to open the 'base' directory again */
 			if (mkdirat(dirfd, base, DEFAULT_LUKS2_LOCK_DIR_PERMS) && errno != EEXIST)
diff --git a/lib/utils_device_locking.h b/lib/utils_device_locking.h
index 41d09349..d38b1a91 100644
--- a/lib/utils_device_locking.h
+++ b/lib/utils_device_locking.h
@@ -22,6 +22,8 @@
 #ifndef _CRYPTSETUP_UTILS_LOCKING_H
 #define _CRYPTSETUP_UTILS_LOCKING_H
 
+#include <stdbool.h>
+
 struct crypt_device;
 struct crypt_lock_handle;
 struct device;
diff --git a/lib/utils_dm.h b/lib/utils_dm.h
index 22add180..c16541cd 100644
--- a/lib/utils_dm.h
+++ b/lib/utils_dm.h
@@ -25,7 +25,8 @@
 #define _UTILS_DM_H
 
 /* device-mapper library helpers */
-#include <inttypes.h>
+#include <stddef.h>
+#include <stdint.h>
 
 struct crypt_device;
 struct volume_key;
@@ -71,6 +72,7 @@ static inline uint32_t act2dmflags(uint32_t act_flags)
 #define DM_INTEGRITY_DISCARDS_SUPPORTED (1 << 23) /* dm-integrity discards/TRIM option is supported */
 #define DM_VERITY_PANIC_CORRUPTION_SUPPORTED (1 << 24) /* dm-verity panic on corruption  */
 #define DM_CRYPT_NO_WORKQUEUE_SUPPORTED (1 << 25) /* dm-crypt suppot for bypassing workqueues  */
+#define DM_INTEGRITY_FIX_HMAC_SUPPORTED (1 << 26) /* hmac covers also superblock */
 
 typedef enum { DM_CRYPT = 0, DM_VERITY, DM_INTEGRITY, DM_LINEAR, DM_ERROR, DM_ZERO, DM_UNKNOWN } dm_target_type;
 enum tdirection { TARGET_SET = 1, TARGET_QUERY };
@@ -120,9 +122,8 @@ struct dm_target {
 		const char *root_hash_sig_key_desc;
 
 		uint64_t hash_offset;	/* hash offset in blocks (not header) */
-		uint64_t hash_blocks;	/* size of hash device (in hash blocks) */
 		uint64_t fec_offset;	/* FEC offset in blocks (not header) */
-		uint64_t fec_blocks;	/* size of FEC device (in hash blocks) */
+		uint64_t fec_blocks;	/* FEC blocks covering data + hash + padding (foreign metadata)*/
 		struct crypt_params_verity *vp;
 	} verity;
 	struct {
@@ -148,6 +149,8 @@ struct dm_target {
 		struct device *meta_device;
 
 		bool fix_padding;
+		bool fix_hmac;
+		bool legacy_recalc;
 	} integrity;
 	struct {
 		uint64_t offset;
@@ -187,8 +190,8 @@ int dm_crypt_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg
 	uint32_t tag_size, uint32_t sector_size);
 int dm_verity_target_set(struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size,
 	struct device *data_device, struct device *hash_device, struct device *fec_device,
-	const char *root_hash, uint32_t root_hash_size, const char *root_hash_sig_key_desc,
-	uint64_t hash_offset_block, uint64_t hash_blocks, struct crypt_params_verity *vp);
+	const char *root_hash, uint32_t root_hash_size, const char* root_hash_sig_key_desc,
+	uint64_t hash_offset_block, uint64_t fec_blocks, struct crypt_params_verity *vp);
 int dm_integrity_target_set(struct crypt_device *cd,
 	struct dm_target *tgt, uint64_t seg_offset, uint64_t seg_size,
 	struct device *meta_device,
diff --git a/lib/utils_safe_memory.c b/lib/utils_safe_memory.c
index 8c1fb5cb..6908ea42 100644
--- a/lib/utils_safe_memory.c
+++ b/lib/utils_safe_memory.c
@@ -66,6 +66,7 @@ void *crypt_safe_alloc(size_t size)
 void crypt_safe_free(void *data)
 {
 	struct safe_allocation *alloc;
+	volatile size_t *s;
 
 	if (!data)
 		return;
@@ -75,7 +76,8 @@ void crypt_safe_free(void *data)
 
 	crypt_safe_memzero(data, alloc->size);
 
-	alloc->size = 0x55aa55aa;
+	s = (volatile size_t *)&alloc->size;
+	*s = 0x55aa55aa;
 	free(alloc);
 }
 
diff --git a/lib/utils_storage_wrappers.c b/lib/utils_storage_wrappers.c
index 09d16117..e7863380 100644
--- a/lib/utils_storage_wrappers.c
+++ b/lib/utils_storage_wrappers.c
@@ -199,7 +199,7 @@ int crypt_storage_wrapper_init(struct crypt_device *cd,
 		goto err;
 	}
 
-	if (!strcmp(_cipher, "cipher_null")) {
+	if (crypt_is_cipher_null(_cipher)) {
 		log_dbg(cd, "Requested cipher_null, switching to noop wrapper.");
 		w->type = NONE;
 		*cw = w;
diff --git a/lib/utils_storage_wrappers.h b/lib/utils_storage_wrappers.h
index f360955b..2519c0af 100644
--- a/lib/utils_storage_wrappers.h
+++ b/lib/utils_storage_wrappers.h
@@ -22,6 +22,9 @@
 #ifndef _UTILS_STORAGE_WRAPPERS_H
 #define _UTILS_STORAGE_WRAPPERS_H
 
+#include <stdint.h>
+#include <sys/types.h>
+
 struct crypt_storage_wrapper;
 struct device;
 struct volume_key;
diff --git a/lib/verity/verity.c b/lib/verity/verity.c
index af31784d..d60af253 100644
--- a/lib/verity/verity.c
+++ b/lib/verity/verity.c
@@ -23,6 +23,7 @@
 #include <stdlib.h>
 #include <string.h>
 #include <stdint.h>
+#include <ctype.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <netinet/in.h>
@@ -145,6 +146,13 @@ int VERITY_read_sb(struct crypt_device *cd,
 	return 0;
 }
 
+static void _to_lower(char *str)
+{
+	for(; *str; str++)
+		if (isupper(*str))
+			*str = tolower(*str);
+}
+
 /* Write verity superblock to disk */
 int VERITY_write_sb(struct crypt_device *cd,
 		   uint64_t sb_offset,
@@ -154,6 +162,7 @@ int VERITY_write_sb(struct crypt_device *cd,
 	struct device *device = crypt_metadata_device(cd);
 	struct verity_sb sb = {};
 	ssize_t hdr_size = sizeof(struct verity_sb);
+	size_t block_size;
 	char *algorithm;
 	uuid_t uuid;
 	int r, devfd;
@@ -173,6 +182,13 @@ int VERITY_write_sb(struct crypt_device *cd,
 		return -EINVAL;
 	}
 
+	/* Avoid possible increasing of image size - FEC could fail later because of it */
+	block_size = device_block_size(cd, device);
+	if (block_size > params->hash_block_size) {
+		device_disable_direct_io(device);
+		block_size = params->hash_block_size;
+	}
+
 	devfd = device_open(cd, device, O_RDWR);
 	if (devfd < 0) {
 		log_err(cd, _("Cannot open device %s."), device_path(device));
@@ -186,13 +202,17 @@ int VERITY_write_sb(struct crypt_device *cd,
 	sb.hash_block_size = cpu_to_le32(params->hash_block_size);
 	sb.salt_size       = cpu_to_le16(params->salt_size);
 	sb.data_blocks     = cpu_to_le64(params->data_size);
+
+	/* Kernel always use lower-case */
 	algorithm = (char *)sb.algorithm;
-	algorithm[sizeof(sb.algorithm)-1] = '\0';
 	strncpy(algorithm, params->hash_name, sizeof(sb.algorithm)-1);
+	algorithm[sizeof(sb.algorithm)-1] = '\0';
+	_to_lower(algorithm);
+
 	memcpy(sb.salt, params->salt, params->salt_size);
 	memcpy(sb.uuid, uuid, sizeof(sb.uuid));
 
-	r = write_lseek_blockwise(devfd, device_block_size(cd, device), device_alignment(device),
+	r = write_lseek_blockwise(devfd, block_size, device_alignment(device),
 				  (char*)&sb, hdr_size, sb_offset) < hdr_size ? -EIO : 0;
 	if (r)
 		log_err(cd, _("Error during update of verity header on device %s."),
@@ -241,7 +261,7 @@ int VERITY_activate(struct crypt_device *cd,
 {
 	uint32_t dmv_flags;
 	unsigned int fec_errors = 0;
-	int r;
+	int r, v;
 	struct crypt_dm_active_device dmd = {
 		.size = verity_hdr->data_size * verity_hdr->data_block_size / 512,
 		.flags = activation_flags,
@@ -260,14 +280,19 @@ int VERITY_activate(struct crypt_device *cd,
 		log_dbg(cd, "Verification of data in userspace required.");
 		r = VERITY_verify(cd, verity_hdr, root_hash, root_hash_size);
 
-		if (r == -EPERM && fec_device) {
+		if ((r == -EPERM || r == -EFAULT) && fec_device) {
+			v = r;
 			log_dbg(cd, "Verification failed, trying to repair with FEC device.");
 			r = VERITY_FEC_process(cd, verity_hdr, fec_device, 1, &fec_errors);
 			if (r < 0)
 				log_err(cd, _("Errors cannot be repaired with FEC device."));
-			else if (fec_errors)
+			else if (fec_errors) {
 				log_err(cd, _("Found %u repairable errors with FEC device."),
 					fec_errors);
+				/* If root hash failed, we cannot be sure it was properly repaired */
+			}
+			if (v == -EFAULT)
+				r = -EPERM;
 		}
 
 		if (r < 0)
@@ -298,7 +323,7 @@ int VERITY_activate(struct crypt_device *cd,
 			crypt_metadata_device(cd), fec_device, root_hash,
 			root_hash_size, signature_description,
 			VERITY_hash_offset_block(verity_hdr),
-			VERITY_hash_blocks(cd, verity_hdr), verity_hdr);
+			VERITY_FEC_blocks(cd, fec_device, verity_hdr), verity_hdr);
 
 	if (r)
 		return r;
diff --git a/lib/verity/verity.h b/lib/verity/verity.h
index 0b7f0cca..f189f460 100644
--- a/lib/verity/verity.h
+++ b/lib/verity/verity.h
@@ -71,6 +71,10 @@ uint64_t VERITY_hash_offset_block(struct crypt_params_verity *params);
 
 uint64_t VERITY_hash_blocks(struct crypt_device *cd, struct crypt_params_verity *params);
 
+uint64_t VERITY_FEC_blocks(struct crypt_device *cd,
+			   struct device *fec_device,
+			   struct crypt_params_verity *params);
+
 int VERITY_UUID_generate(struct crypt_device *cd, char **uuid_string);
 
 #endif
diff --git a/lib/verity/verity_fec.c b/lib/verity/verity_fec.c
index a8a5e86f..5fd82d94 100644
--- a/lib/verity/verity_fec.c
+++ b/lib/verity/verity_fec.c
@@ -134,8 +134,11 @@ static int FEC_process_inputs(struct crypt_device *cd,
 
 	/* calculate the total area covered by error correction codes */
 	ctx.size = 0;
-	for (n = 0; n < ctx.ninputs; ++n)
+	for (n = 0; n < ctx.ninputs; ++n) {
+		log_dbg(cd, "FEC input %s, offset %" PRIu64 " [bytes], length %" PRIu64 " [bytes]",
+			device_path(ctx.inputs[n].device), ctx.inputs[n].start, ctx.inputs[n].count);
 		ctx.size += ctx.inputs[n].count;
+	}
 
 	/* each byte in a data block is covered by a different code */
 	ctx.blocks = FEC_div_round_up(ctx.size, ctx.block_size);
@@ -203,8 +206,8 @@ int VERITY_FEC_process(struct crypt_device *cd,
 		      struct device *fec_device, int check_fec,
 		      unsigned int *errors)
 {
-	int r;
-	int fd = -1;
+	int r = -EIO, fd = -1;
+	size_t ninputs = FEC_INPUT_DEVICES;
 	struct fec_input_device inputs[FEC_INPUT_DEVICES] = {
 		{
 			.device = crypt_data_device(cd),
@@ -214,7 +217,8 @@ int VERITY_FEC_process(struct crypt_device *cd,
 		},{
 			.device = crypt_metadata_device(cd),
 			.fd = -1,
-			.start = VERITY_hash_offset_block(params) * params->data_block_size
+			.start = VERITY_hash_offset_block(params) * params->data_block_size,
+			.count = (VERITY_FEC_blocks(cd, fec_device, params) - params->data_size) * params->data_block_size
 		}
 	};
 
@@ -230,7 +234,12 @@ int VERITY_FEC_process(struct crypt_device *cd,
 		return -EINVAL;
 	}
 
-	r = -EIO;
+	if (!inputs[0].count) {
+		log_err(cd, _("Invalid FEC segment length."));
+		return -EINVAL;
+	}
+	if (!inputs[1].count)
+		ninputs--;
 
 	if (check_fec)
 		fd = open(device_path(fec_device), O_RDONLY);
@@ -259,16 +268,7 @@ int VERITY_FEC_process(struct crypt_device *cd,
 		goto out;
 	}
 
-	/* cover the entire hash device starting from hash_offset */
-	r = device_size(inputs[1].device, &inputs[1].count);
-	if (r) {
-		log_err(cd, _("Failed to determine size for device %s."),
-				device_path(inputs[1].device));
-		goto out;
-	}
-	inputs[1].count -= inputs[1].start;
-
-	r = FEC_process_inputs(cd, params, inputs, FEC_INPUT_DEVICES, fd, check_fec, errors);
+	r = FEC_process_inputs(cd, params, inputs, ninputs, fd, check_fec, errors);
 out:
 	if (inputs[0].fd != -1)
 		close(inputs[0].fd);
@@ -279,3 +279,38 @@ out:
 
 	return r;
 }
+
+uint64_t VERITY_FEC_blocks(struct crypt_device *cd,
+			   struct device *fec_device,
+			   struct crypt_params_verity *params)
+{
+	uint64_t blocks = 0;
+
+	/*
+	* FEC covers this data:
+	*     | protected data | hash area | padding (optional foreign metadata) |
+	*
+	* If hash device is in a separate image, metadata covers the whole rest of the image after hash area.
+	* If hash and FEC device is in the image, metadata ends on the FEC area offset.
+	*/
+	if (device_is_identical(crypt_metadata_device(cd), fec_device) > 0) {
+		log_dbg(cd, "FEC and hash device is the same.");
+		 blocks = params->fec_area_offset;
+	} else {
+		/* cover the entire hash device starting from hash_offset */
+		if (device_size(crypt_metadata_device(cd), &blocks)) {
+			log_err(cd, _("Failed to determine size for device %s."),
+					device_path(crypt_metadata_device(cd)));
+			return 0;
+		}
+	}
+
+	blocks /= params->data_block_size;
+	if (blocks)
+		blocks -= VERITY_hash_offset_block(params);
+
+	/* Protected data */
+	blocks += params->data_size;
+
+	return blocks;
+}
diff --git a/lib/verity/verity_hash.c b/lib/verity/verity_hash.c
index 61424300..e6bc977f 100644
--- a/lib/verity/verity_hash.c
+++ b/lib/verity/verity_hash.c
@@ -89,20 +89,12 @@ out:
 	return r;
 }
 
-static int mult_overflow(off_t *u, off_t b, size_t size)
-{
-	*u = (uint64_t)b * size;
-	if ((off_t)(*u / size) != b || (off_t)*u < 0)
-		return 1;
-	return 0;
-}
-
 static int hash_levels(size_t hash_block_size, size_t digest_size,
-		       off_t data_file_blocks, off_t *hash_position, int *levels,
-		       off_t *hash_level_block, off_t *hash_level_size)
+		       uint64_t data_file_blocks, uint64_t *hash_position, int *levels,
+		       uint64_t *hash_level_block, uint64_t *hash_level_size)
 {
 	size_t hash_per_block_bits;
-	off_t s, s_shift;
+	uint64_t s, s_shift;
 	int i;
 
 	if (!digest_size)
@@ -127,11 +119,10 @@ static int hash_levels(size_t hash_block_size, size_t digest_size,
 		s_shift = (i + 1) * hash_per_block_bits;
 		if (s_shift > 63)
 			return -EINVAL;
-		s = (data_file_blocks + ((off_t)1 << s_shift) - 1) >> ((i + 1) * hash_per_block_bits);
+		s = (data_file_blocks + ((uint64_t)1 << s_shift) - 1) >> ((i + 1) * hash_per_block_bits);
 		if (hash_level_size)
 			hash_level_size[i] = s;
-		if ((*hash_position + s) < *hash_position ||
-		    (*hash_position + s) < 0)
+		if ((*hash_position + s) < *hash_position)
 			return -EINVAL;
 		*hash_position += s;
 	}
@@ -140,9 +131,9 @@ static int hash_levels(size_t hash_block_size, size_t digest_size,
 }
 
 static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
-				   off_t data_block, size_t data_block_size,
-				   off_t hash_block, size_t hash_block_size,
-				   off_t blocks, int version,
+				   uint64_t data_block, size_t data_block_size,
+				   uint64_t hash_block, size_t hash_block_size,
+				   uint64_t blocks, int version,
 				   const char *hash_name, int verify,
 				   char *calculated_digest, size_t digest_size,
 				   const char *salt, size_t salt_size)
@@ -152,14 +143,14 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
 	char read_digest[digest_size];
 	size_t hash_per_block = 1 << get_bits_down(hash_block_size / digest_size);
 	size_t digest_size_full = 1 << get_bits_up(digest_size);
-	off_t blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block;
-	off_t seek_rd, seek_wr;
+	uint64_t blocks_to_write = (blocks + hash_per_block - 1) / hash_per_block;
+	uint64_t seek_rd, seek_wr;
 	size_t left_bytes;
 	unsigned i;
 	int r;
 
-	if (mult_overflow(&seek_rd, data_block, data_block_size) ||
-	    mult_overflow(&seek_wr, hash_block, hash_block_size)) {
+	if (uint64_mult_overflow(&seek_rd, data_block, data_block_size) ||
+	    uint64_mult_overflow(&seek_wr, hash_block, hash_block_size)) {
 		log_err(cd, _("Device offset overflow."));
 		return -EINVAL;
 	}
@@ -241,86 +232,68 @@ static int create_or_verify(struct crypt_device *cd, FILE *rd, FILE *wr,
 	return 0;
 }
 
-static int VERITY_create_or_verify_hash(struct crypt_device *cd,
-	int verify,
-	int version,
-	const char *hash_name,
-	struct device *hash_device,
-	struct device *data_device,
-	size_t hash_block_size,
-	size_t data_block_size,
-	off_t data_blocks,
-	off_t hash_position,
-	char *root_hash,
-	size_t digest_size,
-	const char *salt,
-	size_t salt_size)
+static int VERITY_create_or_verify_hash(struct crypt_device *cd, bool verify,
+	struct crypt_params_verity *params,
+	char *root_hash, size_t digest_size)
 {
 	char calculated_digest[digest_size];
 	FILE *data_file = NULL;
 	FILE *hash_file = NULL, *hash_file_2;
-	off_t hash_level_block[VERITY_MAX_LEVELS];
-	off_t hash_level_size[VERITY_MAX_LEVELS];
-	off_t data_file_blocks;
-	off_t data_device_size = 0, hash_device_size = 0;
+	uint64_t hash_level_block[VERITY_MAX_LEVELS];
+	uint64_t hash_level_size[VERITY_MAX_LEVELS];
+	uint64_t data_file_blocks;
+	uint64_t data_device_offset_max = 0, hash_device_offset_max = 0;
+	uint64_t hash_position = VERITY_hash_offset_block(params);
 	uint64_t dev_size;
 	int levels, i, r;
 
 	log_dbg(cd, "Hash %s %s, data device %s, data blocks %" PRIu64
 		", hash_device %s, offset %" PRIu64 ".",
-		verify ? "verification" : "creation", hash_name,
-		device_path(data_device), data_blocks,
-		device_path(hash_device), hash_position);
-
-	if (data_blocks < 0 || hash_position < 0) {
-		log_err(cd, _("Invalid size parameters for verity device."));
-		return -EINVAL;
-	}
+		verify ? "verification" : "creation", params->hash_name,
+		device_path(crypt_data_device(cd)), params->data_size,
+		device_path(crypt_metadata_device(cd)), hash_position);
 
-	if (!data_blocks) {
-		r = device_size(data_device, &dev_size);
+	if (!params->data_size) {
+		r = device_size(crypt_data_device(cd), &dev_size);
 		if (r < 0)
 			return r;
 
-		data_file_blocks = dev_size / data_block_size;
+		data_file_blocks = dev_size / params->data_block_size;
 	} else
-		data_file_blocks = data_blocks;
+		data_file_blocks = params->data_size;
 
-	if (mult_overflow(&data_device_size, data_blocks, data_block_size)) {
+	if (uint64_mult_overflow(&data_device_offset_max, params->data_size, params->data_block_size)) {
 		log_err(cd, _("Device offset overflow."));
 		return -EINVAL;
 	}
+	log_dbg(cd, "Data device size required: %" PRIu64 " bytes.", data_device_offset_max);
 
-	if (hash_levels(hash_block_size, digest_size, data_file_blocks, &hash_position,
+	if (hash_levels(params->hash_block_size, digest_size, data_file_blocks, &hash_position,
 		&levels, &hash_level_block[0], &hash_level_size[0])) {
 		log_err(cd, _("Hash area overflow."));
 		return -EINVAL;
 	}
-
-	log_dbg(cd, "Using %d hash levels.", levels);
-
-	if (mult_overflow(&hash_device_size, hash_position, hash_block_size)) {
+	if (uint64_mult_overflow(&hash_device_offset_max, hash_position, params->hash_block_size)) {
 		log_err(cd, _("Device offset overflow."));
 		return -EINVAL;
 	}
+	log_dbg(cd, "Hash device size required: %" PRIu64 " bytes.",
+		hash_device_offset_max - params->hash_area_offset);
+	log_dbg(cd, "Using %d hash levels.", levels);
 
-	log_dbg(cd, "Data device size required: %" PRIu64 " bytes.",
-		data_device_size);
-	data_file = fopen(device_path(data_device), "r");
+	data_file = fopen(device_path(crypt_data_device(cd)), "r");
 	if (!data_file) {
 		log_err(cd, _("Cannot open device %s."),
-			device_path(data_device)
+			device_path(crypt_data_device(cd))
 		);
 		r = -EIO;
 		goto out;
 	}
 
-	log_dbg(cd, "Hash device size required: %" PRIu64 " bytes.",
-		hash_device_size);
-	hash_file = fopen(device_path(hash_device), verify ? "r" : "r+");
+	hash_file = fopen(device_path(crypt_metadata_device(cd)), verify ? "r" : "r+");
 	if (!hash_file) {
 		log_err(cd, _("Cannot open device %s."),
-			device_path(hash_device));
+			device_path(crypt_metadata_device(cd)));
 		r = -EIO;
 		goto out;
 	}
@@ -330,25 +303,25 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd,
 	for (i = 0; i < levels; i++) {
 		if (!i) {
 			r = create_or_verify(cd, data_file, hash_file,
-						    0, data_block_size,
-						    hash_level_block[i], hash_block_size,
-						    data_file_blocks, version, hash_name, verify,
-						    calculated_digest, digest_size, salt, salt_size);
+						    0, params->data_block_size,
+						    hash_level_block[i], params->hash_block_size,
+						    data_file_blocks, params->hash_type, params->hash_name, verify,
+						    calculated_digest, digest_size, params->salt, params->salt_size);
 			if (r)
 				goto out;
 		} else {
-			hash_file_2 = fopen(device_path(hash_device), "r");
+			hash_file_2 = fopen(device_path(crypt_metadata_device(cd)), "r");
 			if (!hash_file_2) {
 				log_err(cd, _("Cannot open device %s."),
-					device_path(hash_device));
+					device_path(crypt_metadata_device(cd)));
 				r = -EIO;
 				goto out;
 			}
 			r = create_or_verify(cd, hash_file_2, hash_file,
-						    hash_level_block[i - 1], hash_block_size,
-						    hash_level_block[i], hash_block_size,
-						    hash_level_size[i - 1], version, hash_name, verify,
-						    calculated_digest, digest_size, salt, salt_size);
+						    hash_level_block[i - 1], params->hash_block_size,
+						    hash_level_block[i], params->hash_block_size,
+						    hash_level_size[i - 1], params->hash_type, params->hash_name, verify,
+						    calculated_digest, digest_size, params->salt, params->salt_size);
 			fclose(hash_file_2);
 			if (r)
 				goto out;
@@ -357,23 +330,23 @@ static int VERITY_create_or_verify_hash(struct crypt_device *cd,
 
 	if (levels)
 		r = create_or_verify(cd, hash_file, NULL,
-					    hash_level_block[levels - 1], hash_block_size,
-					    0, hash_block_size,
-					    1, version, hash_name, verify,
-					    calculated_digest, digest_size, salt, salt_size);
+					    hash_level_block[levels - 1], params->hash_block_size,
+					    0, params->hash_block_size,
+					    1, params->hash_type, params->hash_name, verify,
+					    calculated_digest, digest_size, params->salt, params->salt_size);
 	else
 		r = create_or_verify(cd, data_file, NULL,
-					    0, data_block_size,
-					    0, hash_block_size,
-					    data_file_blocks, version, hash_name, verify,
-					    calculated_digest, digest_size, salt, salt_size);
+					    0, params->data_block_size,
+					    0, params->hash_block_size,
+					    data_file_blocks, params->hash_type, params->hash_name, verify,
+					    calculated_digest, digest_size, params->salt, params->salt_size);
 out:
 	if (verify) {
 		if (r)
 			log_err(cd, _("Verification of data area failed."));
 		else {
 			log_dbg(cd, "Verification of data area succeeded.");
-			r = memcmp(root_hash, calculated_digest, digest_size) ? -EPERM : 0;
+			r = memcmp(root_hash, calculated_digest, digest_size) ? -EFAULT : 0;
 			if (r)
 				log_err(cd, _("Verification of root hash failed."));
 			else
@@ -403,19 +376,7 @@ int VERITY_verify(struct crypt_device *cd,
 		  const char *root_hash,
 		  size_t root_hash_size)
 {
-	return VERITY_create_or_verify_hash(cd, 1,
-		verity_hdr->hash_type,
-		verity_hdr->hash_name,
-		crypt_metadata_device(cd),
-		crypt_data_device(cd),
-		verity_hdr->hash_block_size,
-		verity_hdr->data_block_size,
-		verity_hdr->data_size,
-		VERITY_hash_offset_block(verity_hdr),
-		CONST_CAST(char*)root_hash,
-		root_hash_size,
-		verity_hdr->salt,
-		verity_hdr->salt_size);
+	return VERITY_create_or_verify_hash(cd, 1, verity_hdr, CONST_CAST(char*)root_hash, root_hash_size);
 }
 
 /* Create verity hash */
@@ -433,24 +394,12 @@ int VERITY_create(struct crypt_device *cd,
 		log_err(cd, _("WARNING: Kernel cannot activate device if data "
 			      "block size exceeds page size (%u)."), pgsize);
 
-	return VERITY_create_or_verify_hash(cd, 0,
-		verity_hdr->hash_type,
-		verity_hdr->hash_name,
-		crypt_metadata_device(cd),
-		crypt_data_device(cd),
-		verity_hdr->hash_block_size,
-		verity_hdr->data_block_size,
-		verity_hdr->data_size,
-		VERITY_hash_offset_block(verity_hdr),
-		CONST_CAST(char*)root_hash,
-		root_hash_size,
-		verity_hdr->salt,
-		verity_hdr->salt_size);
+	return VERITY_create_or_verify_hash(cd, 0, verity_hdr, CONST_CAST(char*)root_hash, root_hash_size);
 }
 
 uint64_t VERITY_hash_blocks(struct crypt_device *cd, struct crypt_params_verity *params)
 {
-	off_t hash_position = 0;
+	uint64_t hash_position = 0;
 	int levels = 0;
 
 	if (hash_levels(params->hash_block_size, crypt_get_volume_key_size(cd),
diff --git a/src/cryptsetup.c b/src/cryptsetup.c
index b32f167d..70e1f390 100644
--- a/src/cryptsetup.c
+++ b/src/cryptsetup.c
@@ -24,22 +24,43 @@
 #include "cryptsetup.h"
 #include <uuid/uuid.h>
 
-static const char *opt_cipher = NULL;
-static const char *opt_keyslot_cipher = NULL;
-static const char *opt_hash = NULL;
-static int opt_verify_passphrase = 0;
+static char *opt_cipher = NULL;
+static char *opt_keyslot_cipher = NULL;
+static char *opt_hash = NULL;
+static char *opt_json_file = NULL;
+static char *opt_key_file = NULL;
+static char *opt_keyfile_stdin = NULL;
+static char *opt_keyfiles[MAX_KEYFILES];
+static char *opt_master_key_file = NULL;
+static char *opt_header_backup_file = NULL;
+static char *opt_uuid = NULL;
+static char *opt_header_device = NULL;
+static char *opt_type = NULL;
+static char *opt_pbkdf = NULL;
+static char *opt_priority = NULL; /* normal */
+static char *opt_integrity = NULL; /* none */
+static char *opt_key_description = NULL;
+static char *opt_label = NULL;
+static char *opt_subsystem = NULL;
+static char *opt_active_name = NULL;
+static char *opt_resilience_mode = NULL; /* default value "checksum" */
+static char *opt_resilience_hash = NULL; /* default value "sha256" */
+
+/* helper strings converted to uint64_t later */
+static char *opt_reduce_size_str = NULL;
+static char *opt_hotzone_size_str = NULL;
+static char *opt_device_size_str = NULL;
+static char *opt_luks2_metadata_size_str = NULL;
+static char *opt_luks2_keyslots_size_str = NULL;
 
-static const char *opt_json_file = NULL;
-static const char *opt_key_file = NULL;
-static const char *opt_keyfile_stdin = NULL;
-static int opt_keyfiles_count = 0;
-static const char *opt_keyfiles[MAX_KEYFILES];
+static uint64_t opt_reduce_size = 0;
+static uint64_t opt_hotzone_size = 0;
+static uint64_t opt_device_size = 0;
+static uint64_t opt_luks2_metadata_size = 0;
+static uint64_t opt_luks2_keyslots_size = 0;
 
-static const char *opt_master_key_file = NULL;
-static const char *opt_header_backup_file = NULL;
-static const char *opt_uuid = NULL;
-static const char *opt_header_device = NULL;
-static const char *opt_type = "luks";
+static int opt_keyfiles_count = 0;
+static int opt_verify_passphrase = 0;
 static int opt_key_size = 0;
 static int opt_keyslot_key_size = 0;
 static long opt_keyfile_size = 0;
@@ -76,57 +97,68 @@ static int opt_veracrypt_query_pim = 0;
 static int opt_deferred_remove = 0;
 static int opt_serialize_memory_hard_pbkdf = 0;
 //FIXME: check uint32 overflow for long type
-static const char *opt_pbkdf = NULL;
 static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB;
 static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS;
 static long opt_pbkdf_iterations = 0;
 static int opt_iteration_time = 0;
 static int opt_disable_locks = 0;
 static int opt_disable_keyring = 0;
-static const char *opt_priority = NULL; /* normal */
-static const char *opt_integrity = NULL; /* none */
 static int opt_integrity_nojournal = 0;
 static int opt_integrity_no_wipe = 0;
 static int opt_integrity_legacy_padding = 0;
-static const char *opt_key_description = NULL;
 static int opt_sector_size = 0;
 static int opt_iv_large_sectors = 0;
 static int opt_persistent = 0;
-static const char *opt_label = NULL;
-static const char *opt_subsystem = NULL;
 static int opt_unbound = 0;
 static int opt_refresh = 0;
 
 /* LUKS2 reencryption parameters */
-static const char *opt_active_name = NULL;
-static const char *opt_resilience_mode = "checksum"; // TODO: default resilience
-static const char *opt_resilience_hash = "sha256"; // TODO: default checksum hash
 static int opt_encrypt = 0;
 static int opt_reencrypt_init_only = 0;
 static int opt_reencrypt_resume_only = 0;
 static int opt_decrypt = 0;
 
-static const char *opt_reduce_size_str = NULL;
-static uint64_t opt_reduce_size = 0;
-
-static const char *opt_hotzone_size_str = NULL;
-static uint64_t opt_hotzone_size = 0;
-
-static const char *opt_device_size_str = NULL;
-static uint64_t opt_device_size = 0;
-
 /* do not set from command line, use helpers above */
 static int64_t opt_data_shift;
-
-static const char *opt_luks2_metadata_size_str = NULL;
-static uint64_t opt_luks2_metadata_size = 0;
-static const char *opt_luks2_keyslots_size_str = NULL;
-static uint64_t opt_luks2_keyslots_size = 0;
+static const char *device_type = "luks";
+static const char *set_pbkdf = NULL;
 
 static const char **action_argv;
 static int action_argc;
 static const char *null_action_argv[] = {NULL, NULL};
 
+void tools_cleanup(void)
+{
+	FREE_AND_NULL(opt_cipher);
+	FREE_AND_NULL(opt_keyslot_cipher);
+	FREE_AND_NULL(opt_hash);
+	FREE_AND_NULL(opt_json_file);
+	FREE_AND_NULL(opt_key_file);
+	FREE_AND_NULL(opt_keyfile_stdin);
+	FREE_AND_NULL(opt_master_key_file);
+	FREE_AND_NULL(opt_header_backup_file);
+	FREE_AND_NULL(opt_uuid);
+	FREE_AND_NULL(opt_header_device);
+	FREE_AND_NULL(opt_type);
+	FREE_AND_NULL(opt_pbkdf);
+	FREE_AND_NULL(opt_priority);
+	FREE_AND_NULL(opt_integrity);
+	FREE_AND_NULL(opt_key_description);
+	FREE_AND_NULL(opt_label);
+	FREE_AND_NULL(opt_subsystem);
+	FREE_AND_NULL(opt_active_name);
+	FREE_AND_NULL(opt_resilience_mode);
+	FREE_AND_NULL(opt_resilience_hash);
+	FREE_AND_NULL(opt_reduce_size_str);
+	FREE_AND_NULL(opt_hotzone_size_str);
+	FREE_AND_NULL(opt_device_size_str);
+	FREE_AND_NULL(opt_luks2_metadata_size_str);
+	FREE_AND_NULL(opt_luks2_keyslots_size_str);
+
+	while (opt_keyfiles_count)
+		free(opt_keyfiles[--opt_keyfiles_count]);
+}
+
 static const char *uuid_or_device_header(const char **data_device)
 {
 	if (data_device)
@@ -440,7 +472,7 @@ static int tcrypt_load(struct crypt_device *cd, struct crypt_params_tcrypt *para
 			unsigned long long tmp_pim_ull = 0;
 
 			r = tools_get_key(_("Enter VeraCrypt PIM: "),
-					CONST_CAST(char**)&tmp_pim_nptr,
+					&tmp_pim_nptr,
 					&tmp_pim_size, 0, 0, opt_keyfile_stdin, opt_timeout,
 					_verify_passphrase(0), 0, cd);
 			if (r < 0)
@@ -457,7 +489,7 @@ static int tcrypt_load(struct crypt_device *cd, struct crypt_params_tcrypt *para
 				log_err(_("Invalid PIM value: outside of range."));
 				r = -ERANGE;
 			}
-			crypt_safe_free(CONST_CAST(char*)tmp_pim_nptr);
+			crypt_safe_free(tmp_pim_nptr);
 			if (r < 0)
 				continue;
 
@@ -500,7 +532,7 @@ static int action_open_tcrypt(void)
 {
 	struct crypt_device *cd = NULL;
 	struct crypt_params_tcrypt params = {
-		.keyfiles = opt_keyfiles,
+		.keyfiles = CONST_CAST(const char **)opt_keyfiles,
 		.keyfiles_count = opt_keyfiles_count,
 		.flags = CRYPT_TCRYPT_LEGACY_MODES |
 			 (opt_veracrypt ? CRYPT_TCRYPT_VERA_MODES : 0),
@@ -618,7 +650,7 @@ static int action_tcryptDump(void)
 {
 	struct crypt_device *cd = NULL;
 	struct crypt_params_tcrypt params = {
-		.keyfiles = opt_keyfiles,
+		.keyfiles = CONST_CAST(const char **)opt_keyfiles,
 		.keyfiles_count = opt_keyfiles_count,
 		.flags = CRYPT_TCRYPT_LEGACY_MODES |
 			 (opt_veracrypt ? CRYPT_TCRYPT_VERA_MODES : 0),
@@ -715,7 +747,9 @@ static int action_resize(void)
 		r = crypt_activate_by_token(cd, NULL, opt_token, NULL,
 					    CRYPT_ACTIVATE_KEYRING_KEY);
 		tools_keyslot_msg(r, UNLOCKED);
-		if (r < 0 && opt_token_only)
+		if (r >= 0)
+			goto resize;
+		else if (opt_token_only)
 			goto out;
 
 		r = tools_get_key(NULL, &password, &passwordLen,
@@ -731,7 +765,7 @@ static int action_resize(void)
 		tools_keyslot_msg(r, UNLOCKED);
 		crypt_safe_free(password);
 	}
-
+resize:
 	if (opt_device_size)
 		opt_size = opt_device_size / SECTOR_SIZE;
 
@@ -961,10 +995,10 @@ static int action_benchmark(void)
 	int i, r;
 
 	log_std(_("# Tests are approximate using memory only (no storage IO).\n"));
-	if (opt_pbkdf || opt_hash) {
-		if (!opt_pbkdf && opt_hash)
-			opt_pbkdf = CRYPT_KDF_PBKDF2;
-		r = action_benchmark_kdf(opt_pbkdf, opt_hash, key_size);
+	if (set_pbkdf || opt_hash) {
+		if (!set_pbkdf && opt_hash)
+			set_pbkdf = CRYPT_KDF_PBKDF2;
+		r = action_benchmark_kdf(set_pbkdf, opt_hash, key_size);
 	} else if (opt_cipher) {
 		r = crypt_parse_name_and_mode(opt_cipher, cipher, NULL, cipher_mode);
 		if (r < 0) {
@@ -1036,7 +1070,7 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type)
 	if (!pbkdf_default)
 		return -EINVAL;
 
-	pbkdf.type = opt_pbkdf ?: pbkdf_default->type;
+	pbkdf.type = set_pbkdf ?: pbkdf_default->type;
 	pbkdf.hash = opt_hash ?: pbkdf_default->hash;
 	pbkdf.time_ms = (uint32_t)opt_iteration_time ?: pbkdf_default->time_ms;
 	if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) {
@@ -1063,11 +1097,17 @@ static int set_keyslot_params(struct crypt_device *cd, int keyslot)
 	if (!cipher)
 		return -EINVAL;
 
+	if (crypt_is_cipher_null(cipher)) {
+		log_dbg("Keyslot %d uses cipher_null. Replacing with default encryption in new keyslot.", keyslot);
+		cipher = DEFAULT_LUKS2_KEYSLOT_CIPHER;
+		key_size = DEFAULT_LUKS2_KEYSLOT_KEYBITS / 8;
+	}
+
 	if (crypt_keyslot_set_encryption(cd, cipher, key_size))
 		return -EINVAL;
 
 	/* if requested any of those just reinitialize context pbkdf */
-	if (opt_pbkdf || opt_hash || opt_pbkdf_iterations || opt_iteration_time)
+	if (set_pbkdf || opt_hash || opt_pbkdf_iterations || opt_iteration_time)
 		return set_pbkdf_params(cd, CRYPT_LUKS2);
 
 	if (crypt_keyslot_get_pbkdf(cd, keyslot, &pbkdf))
@@ -1139,7 +1179,7 @@ static int action_luksRepair(void)
 		goto out;
 
 	crypt_set_log_callback(cd, quiet_log, NULL);
-	r = crypt_load(cd, luksType(opt_type), NULL);
+	r = crypt_load(cd, luksType(device_type), NULL);
 	crypt_set_log_callback(cd, tool_log, NULL);
 	if (r == 0) {
 		log_verbose(_("No known problems detected for LUKS header."));
@@ -1153,7 +1193,7 @@ static int action_luksRepair(void)
 	r = yesDialog(_("Really try to repair LUKS device header?"),
 		       _("Operation aborted.\n")) ? 0 : -EINVAL;
 	if (r == 0)
-		r = crypt_repair(cd, luksType(opt_type), NULL);
+		r = crypt_repair(cd, luksType(device_type), NULL);
 skip_repair:
 	if (!r && crypt_get_type(cd) && !strcmp(crypt_get_type(cd), CRYPT_LUKS2))
 		r = _do_luks2_reencrypt_recovery(cd);
@@ -1202,6 +1242,21 @@ static int strcmp_or_null(const char *str, const char *expected)
 	return !str ? 0 : strcmp(str, expected);
 }
 
+static int get_adjusted_key_size(const char *cipher_mode, uint32_t default_size_bits, int integrity_keysize)
+{
+	uint32_t keysize_bits = opt_key_size;
+
+#ifdef ENABLE_LUKS_ADJUST_XTS_KEYSIZE
+	if (!opt_key_size && !strncmp(cipher_mode, "xts-", 4)) {
+		if (default_size_bits == 128)
+			keysize_bits = 256;
+		else if (default_size_bits == 256)
+			keysize_bits = 512;
+	}
+#endif
+	return (keysize_bits ?: default_size_bits) / 8 + integrity_keysize;
+}
+
 static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_passwordLen)
 {
 	int r = -EINVAL, keysize, integrity_keysize = 0, fd, created = 0;
@@ -1225,7 +1280,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_
 	};
 	void *params;
 
-	type = luksType(opt_type);
+	type = luksType(device_type);
 	if (!type)
 		type = crypt_get_default_type();
 
@@ -1254,7 +1309,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_
 	/* Create header file (must contain at least one sector)? */
 	if (opt_header_device && stat(opt_header_device, &st) < 0 && errno == ENOENT) {
 		if (!opt_batch_mode &&
-		    !yesDialog("Header file does not exist, do you want to create it?",
+		    !yesDialog(_("Header file does not exist, do you want to create it?"),
 			       _("Operation aborted.\n")))
 		    return -EPERM;
 
@@ -1293,7 +1348,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_
 	}
 
 	/* Never call pwquality if using null cipher */
-	if (tools_is_cipher_null(cipher))
+	if (crypt_is_cipher_null(cipher))
 		opt_force_password = 1;
 
 	if ((r = crypt_init(&cd, header_device))) {
@@ -1334,15 +1389,7 @@ static int _luksFormat(struct crypt_device **r_cd, char **r_password, size_t *r_
 			goto out;
 	}
 
-#ifdef ENABLE_LUKS_ADJUST_XTS_KEYSIZE
-	if (!opt_key_size && !strncmp(cipher_mode, "xts-", 4)) {
-		if (DEFAULT_LUKS1_KEYBITS == 128)
-			opt_key_size = 256;
-		else if (DEFAULT_LUKS1_KEYBITS == 256)
-			opt_key_size = 512;
-	}
-#endif
-	keysize = (opt_key_size ?: DEFAULT_LUKS1_KEYBITS) / 8 + integrity_keysize;
+	keysize = get_adjusted_key_size(cipher_mode, DEFAULT_LUKS1_KEYBITS, integrity_keysize);
 
 	if (opt_random)
 		crypt_set_rng_type(cd, CRYPT_RNG_RANDOM);
@@ -1441,7 +1488,7 @@ static int action_open_luks(void)
 		if ((r = crypt_init_data_device(&cd, header_device, data_device)))
 			goto out;
 
-		if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+		if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 			log_err(_("Device %s is not a valid LUKS device."),
 				header_device);
 			goto out;
@@ -1567,7 +1614,7 @@ static int action_luksKillSlot(void)
 
 	crypt_set_confirm_callback(cd, yesDialog, NULL);
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -1624,7 +1671,7 @@ static int action_luksRemoveKey(void)
 
 	crypt_set_confirm_callback(cd, yesDialog, NULL);
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -1691,7 +1738,7 @@ static int luksAddUnboundKey(void)
 		goto out;
 
 	/* Never call pwquality if using null cipher */
-	if (tools_is_cipher_null(crypt_get_cipher(cd)))
+	if (crypt_is_cipher_null(crypt_get_cipher(cd)))
 		opt_force_password = 1;
 
 	keysize = opt_key_size / 8;
@@ -1747,7 +1794,7 @@ static int action_luksAddKey(void)
 
 	crypt_set_confirm_callback(cd, yesDialog, NULL);
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -1758,7 +1805,7 @@ static int action_luksAddKey(void)
 		goto out;
 
 	/* Never call pwquality if using null cipher */
-	if (tools_is_cipher_null(crypt_get_cipher(cd)))
+	if (crypt_is_cipher_null(crypt_get_cipher(cd)))
 		opt_force_password = 1;
 
 	keysize = crypt_get_volume_key_size(cd);
@@ -1850,7 +1897,7 @@ static int action_luksChangeKey(void)
 	if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
 		goto out;
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -1861,7 +1908,7 @@ static int action_luksChangeKey(void)
 		goto out;
 
 	/* Never call pwquality if using null cipher */
-	if (tools_is_cipher_null(crypt_get_cipher(cd)))
+	if (crypt_is_cipher_null(crypt_get_cipher(cd)))
 		opt_force_password = 1;
 
 	r = set_pbkdf_params(cd, crypt_get_type(cd));
@@ -1968,7 +2015,7 @@ static int action_isLuks(void)
 		goto out;
 
 	crypt_set_log_callback(cd, quiet_log, NULL);
-	r = crypt_load(cd, luksType(opt_type), NULL);
+	r = crypt_load(cd, luksType(device_type), NULL);
 out:
 	crypt_free(cd);
 	return r;
@@ -1985,7 +2032,7 @@ static int action_luksUUID(void)
 
 	crypt_set_confirm_callback(cd, yesDialog, _("Operation aborted.\n"));
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL)))
+	if ((r = crypt_load(cd, luksType(device_type), NULL)))
 		goto out;
 
 	if (opt_uuid)
@@ -2144,7 +2191,7 @@ static int action_luksDump(void)
 	if ((r = crypt_init(&cd, uuid_or_device_header(NULL))))
 		goto out;
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -2167,8 +2214,11 @@ static int action_luksSuspend(void)
 	int r;
 
 	r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device));
-	if (!r)
+	if (!r) {
 		r = crypt_suspend(cd, action_argv[0]);
+		if (r == -ENODEV)
+			log_err(_("%s is not active %s device name."), action_argv[0], "LUKS");
+	}
 
 	crypt_free(cd);
 	return r;
@@ -2180,12 +2230,25 @@ static int action_luksResume(void)
 	char *password = NULL;
 	size_t passwordLen;
 	int r, tries;
+	const char *type, *req_type = luksType(device_type);
+
+	if (req_type && strcmp(req_type, CRYPT_LUKS1) && strcmp(req_type, CRYPT_LUKS2))
+		return -EINVAL;
 
 	if ((r = crypt_init_by_name_and_header(&cd, action_argv[0], uuid_or_device(opt_header_device))))
+		return r;
+
+	r = -EINVAL;
+	type = crypt_get_type(cd);
+	if (!type || (strcmp(type, CRYPT_LUKS1) && strcmp(type, CRYPT_LUKS2))) {
+		log_err(_("%s is not active LUKS device name or header is missing."), action_argv[0]);
 		goto out;
+	}
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL)))
+	if (req_type && strcmp(req_type, crypt_get_type(cd))) {
+		log_err(_("%s is not active %s device name."), action_argv[0], req_type);
 		goto out;
+	}
 
 	tries = (tools_is_stdin(opt_key_file) && isatty(STDIN_FILENO)) ? opt_tries : 1;
 	do {
@@ -2289,38 +2352,38 @@ static const char *_get_device_type(void)
 
 static int action_open(void)
 {
-	if (opt_refresh && !opt_type)
+	if (opt_refresh && !device_type)
 		/* read device type from active mapping */
-		opt_type = _get_device_type();
+		device_type = _get_device_type();
 
-	if (!opt_type)
+	if (!device_type)
 		return -EINVAL;
 
-	if (!strcmp(opt_type, "luks") ||
-	    !strcmp(opt_type, "luks1") ||
-	    !strcmp(opt_type, "luks2")) {
+	if (!strcmp(device_type, "luks") ||
+	    !strcmp(device_type, "luks1") ||
+	    !strcmp(device_type, "luks2")) {
 		if (action_argc < 2 && (!opt_test_passphrase && !opt_refresh))
 			goto args;
 		return action_open_luks();
-	} else if (!strcmp(opt_type, "plain")) {
+	} else if (!strcmp(device_type, "plain")) {
 		if (action_argc < 2 && !opt_refresh)
 			goto args;
 		return action_open_plain();
-	} else if (!strcmp(opt_type, "loopaes")) {
+	} else if (!strcmp(device_type, "loopaes")) {
 		if (action_argc < 2 && !opt_refresh)
 			goto args;
 		return action_open_loopaes();
-	} else if (!strcmp(opt_type, "tcrypt")) {
+	} else if (!strcmp(device_type, "tcrypt")) {
 		if (action_argc < 2 && !opt_test_passphrase)
 			goto args;
 		return action_open_tcrypt();
-	} else if (!strcmp(opt_type, "bitlk")) {
+	} else if (!strcmp(device_type, "bitlk")) {
 		if (action_argc < 2 && !opt_test_passphrase)
 			goto args;
 		return action_open_bitlk();
 	}
 
-	log_err(_("Unrecognized metadata device type %s."), opt_type);
+	log_err(_("Unrecognized metadata device type %s."), device_type);
 	return -EINVAL;
 args:
 	log_err(_("Command requires device and mapped name as arguments."));
@@ -2339,7 +2402,7 @@ static int action_luksErase(void)
 
 	crypt_set_confirm_callback(cd, yesDialog, NULL);
 
-	if ((r = crypt_load(cd, luksType(opt_type), NULL))) {
+	if ((r = crypt_load(cd, luksType(device_type), NULL))) {
 		log_err(_("Device %s is not a valid LUKS device."),
 			uuid_or_device_header(NULL));
 		goto out;
@@ -2359,8 +2422,10 @@ static int action_luksErase(void)
 
 	/* Safety check */
 	max = crypt_keyslot_max(crypt_get_type(cd));
-	if (max <= 0)
-		return -EINVAL;
+	if (max <= 0) {
+		r = -EINVAL;
+		goto out;
+	}
 
 	for (i = 0; i < max; i++) {
 		ki = crypt_keyslot_status(cd, i);
@@ -2384,9 +2449,9 @@ static int action_luksConvert(void)
 	const char *to_type, *from_type;
 	int r;
 
-	if (!strcmp(opt_type, "luks2")) {
+	if (!strcmp(device_type, "luks2")) {
 		to_type = CRYPT_LUKS2;
-	} else if (!strcmp(opt_type, "luks1")) {
+	} else if (!strcmp(device_type, "luks1")) {
 		to_type = CRYPT_LUKS1;
 	} else {
 		log_err(_("Invalid LUKS type, only luks1 and luks2 are supported."));
@@ -2705,8 +2770,8 @@ static int action_reencrypt_load(struct crypt_device *cd)
 	char dm_name[PATH_MAX] = {}, *password = NULL;
 	const char *active_name = NULL;
 	struct crypt_params_reencrypt params = {
-		.resilience = opt_resilience_mode,
-		.hash = opt_resilience_hash,
+		.resilience = opt_resilience_mode ?: "checksum",
+		.hash = opt_resilience_hash ?: "sha256",
 		.max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
 		.device_size = opt_device_size / SECTOR_SIZE,
 		.flags = CRYPT_REENCRYPT_RESUME_ONLY
@@ -2750,8 +2815,8 @@ static int action_encrypt_luks2(struct crypt_device **cd)
 	struct crypt_params_reencrypt params = {
 		.mode = CRYPT_REENCRYPT_ENCRYPT,
 		.direction = opt_data_shift < 0 ? CRYPT_REENCRYPT_BACKWARD : CRYPT_REENCRYPT_FORWARD,
-		.resilience = opt_resilience_mode,
-		.hash = opt_resilience_hash,
+		.resilience = opt_resilience_mode ?: "checksum",
+		.hash = opt_resilience_hash ?: "sha256",
 		.max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
 		.device_size = opt_device_size / SECTOR_SIZE,
 		.luks2 = &luks2_params,
@@ -2760,7 +2825,7 @@ static int action_encrypt_luks2(struct crypt_device **cd)
 
 	_set_reencryption_flags(&params.flags);
 
-	type = luksType(opt_type);
+	type = luksType(device_type);
 	if (!type)
 		type = crypt_get_default_type();
 
@@ -2800,7 +2865,8 @@ static int action_encrypt_luks2(struct crypt_device **cd)
 	if (!opt_uuid) {
 		uuid_generate(uuid);
 		uuid_unparse(uuid, uuid_str);
-		opt_uuid = uuid_str;
+		if (!(opt_uuid = strdup(uuid_str)))
+			return -ENOMEM;
 	}
 
 	/* Check the data device is not LUKS device already */
@@ -2839,7 +2905,10 @@ static int action_encrypt_luks2(struct crypt_device **cd)
 			goto err;
 		}
 
-		opt_header_device = header_file;
+		if (!(opt_header_device = strdup(header_file))) {
+			r = -ENOMEM;
+			goto err;
+		}
 		/*
 		 * FIXME: just override offset here, but we should support both.
 		 * offset and implicit offset via data shift (lvprepend?)
@@ -2925,8 +2994,8 @@ static int action_decrypt_luks2(struct crypt_device *cd)
 	struct crypt_params_reencrypt params = {
 		.mode = CRYPT_REENCRYPT_DECRYPT,
 		.direction = opt_data_shift > 0 ? CRYPT_REENCRYPT_FORWARD : CRYPT_REENCRYPT_BACKWARD,
-		.resilience = opt_data_shift ? "datashift" : opt_resilience_mode,
-		.hash = opt_resilience_hash,
+		.resilience = opt_data_shift ? "datashift" : (opt_resilience_mode ?: "checksum"),
+		.hash = opt_resilience_hash ?: "sha256",
 		.data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE,
 		.device_size = opt_device_size / SECTOR_SIZE,
 		.max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
@@ -3138,8 +3207,8 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
 	struct crypt_params_reencrypt params = {
 		.mode = CRYPT_REENCRYPT_REENCRYPT,
 		.direction = opt_data_shift < 0 ? CRYPT_REENCRYPT_BACKWARD : CRYPT_REENCRYPT_FORWARD,
-		.resilience = opt_data_shift ? "datashift" : opt_resilience_mode,
-		.hash = opt_resilience_hash,
+		.resilience = opt_data_shift ? "datashift" : (opt_resilience_mode ?: "checksum"),
+		.hash = opt_resilience_hash ?: "sha256",
 		.data_shift = imaxabs(opt_data_shift) / SECTOR_SIZE,
 		.max_hotzone_size = opt_hotzone_size / SECTOR_SIZE,
 		.device_size = opt_device_size / SECTOR_SIZE,
@@ -3148,6 +3217,11 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
 
 	_set_reencryption_flags(&params.flags);
 
+	if (!opt_cipher && crypt_is_cipher_null(crypt_get_cipher(cd))) {
+		opt_cipher = strdup(DEFAULT_CIPHER(LUKS1));
+		log_std(_("Switching data encryption cipher to %s.\n"), opt_cipher);
+	}
+
 	if (!opt_cipher) {
 		strncpy(cipher, crypt_get_cipher(cd), MAX_CIPHER_LEN - 1);
 		strncpy(mode, crypt_get_cipher_mode(cd), MAX_CIPHER_LEN - 1);
@@ -3164,10 +3238,8 @@ static int action_reencrypt_luks2(struct crypt_device *cd)
 	if (r)
 		return r;
 
-	if (opt_key_size)
-		key_size = opt_key_size / 8;
-	else if (opt_cipher)
-		key_size = DEFAULT_LUKS1_KEYBITS / 8;
+	if (opt_key_size || opt_cipher)
+		key_size = get_adjusted_key_size(mode, DEFAULT_LUKS1_KEYBITS, 0);
 	else
 		key_size = crypt_get_volume_key_size(cd);
 
@@ -3450,10 +3522,12 @@ static void help(poptContext popt_context,
 #if defined(ENABLE_LUKS_ADJUST_XTS_KEYSIZE) && DEFAULT_LUKS1_KEYBITS != 512
 		log_std(_("\tLUKS: Default keysize with XTS mode (two internal keys) will be doubled.\n"));
 #endif
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else if (key->shortName == 'V') {
 		log_std("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else
@@ -3494,7 +3568,6 @@ static int run_action(struct action_type *action)
 
 int main(int argc, const char **argv)
 {
-	static char *popt_tmp;
 	static struct poptOption popt_help_options[] = {
 		{ NULL,    '\0', POPT_ARG_CALLBACK, help, 0, NULL,                         NULL },
 		{ "help",  '?',  POPT_ARG_NONE,     NULL, 0, N_("Show this help message"), NULL },
@@ -3510,19 +3583,19 @@ int main(int argc, const char **argv)
 		{ "cipher",            'c',  POPT_ARG_STRING, &opt_cipher,              0, N_("The cipher used to encrypt the disk (see /proc/crypto)"), NULL },
 		{ "hash",              'h',  POPT_ARG_STRING, &opt_hash,                0, N_("The hash used to create the encryption key from the passphrase"), NULL },
 		{ "verify-passphrase", 'y',  POPT_ARG_NONE, &opt_verify_passphrase,     0, N_("Verifies the passphrase by asking for it twice"), NULL },
-		{ "key-file",          'd',  POPT_ARG_STRING, &opt_key_file,            6, N_("Read the key from a file"), NULL },
+		{ "key-file",          'd',  POPT_ARG_STRING, NULL,                     6, N_("Read the key from a file"), NULL },
 		{ "master-key-file",  '\0',  POPT_ARG_STRING, &opt_master_key_file,     0, N_("Read the volume (master) key from file."), NULL },
 		{ "dump-master-key",  '\0',  POPT_ARG_NONE, &opt_dump_master_key,       0, N_("Dump volume (master) key instead of keyslots info"), NULL },
 		{ "key-size",          's',  POPT_ARG_INT, &opt_key_size,               0, N_("The size of the encryption key"), N_("BITS") },
 		{ "keyfile-size",      'l',  POPT_ARG_LONG, &opt_keyfile_size,          0, N_("Limits the read from keyfile"), N_("bytes") },
-		{ "keyfile-offset",   '\0',  POPT_ARG_STRING, &popt_tmp,                4, N_("Number of bytes to skip in keyfile"), N_("bytes") },
+		{ "keyfile-offset",   '\0',  POPT_ARG_STRING, NULL,                     4, N_("Number of bytes to skip in keyfile"), N_("bytes") },
 		{ "new-keyfile-size", '\0',  POPT_ARG_LONG, &opt_new_keyfile_size,      0, N_("Limits the read from newly added keyfile"), N_("bytes") },
-		{ "new-keyfile-offset",'\0', POPT_ARG_STRING, &popt_tmp,                5, N_("Number of bytes to skip in newly added keyfile"), N_("bytes") },
+		{ "new-keyfile-offset",'\0', POPT_ARG_STRING, NULL,                     5, N_("Number of bytes to skip in newly added keyfile"), N_("bytes") },
 		{ "key-slot",          'S',  POPT_ARG_INT, &opt_key_slot,               0, N_("Slot number for new key (default is first free)"), NULL },
-		{ "size",              'b',  POPT_ARG_STRING, &popt_tmp,                1, N_("The size of the device"), N_("SECTORS") },
+		{ "size",              'b',  POPT_ARG_STRING, NULL,                     1, N_("The size of the device"), N_("SECTORS") },
 		{ "device-size",      '\0',  POPT_ARG_STRING, &opt_device_size_str,     0, N_("Use only specified device size (ignore rest of device). DANGEROUS!"), N_("bytes") },
-		{ "offset",            'o',  POPT_ARG_STRING, &popt_tmp,                2, N_("The start offset in the backend device"), N_("SECTORS") },
-		{ "skip",              'p',  POPT_ARG_STRING, &popt_tmp,                3, N_("How many sectors of the encrypted data to skip at the beginning"), N_("SECTORS") },
+		{ "offset",            'o',  POPT_ARG_STRING, NULL,                     2, N_("The start offset in the backend device"), N_("SECTORS") },
+		{ "skip",              'p',  POPT_ARG_STRING, NULL,                     3, N_("How many sectors of the encrypted data to skip at the beginning"), N_("SECTORS") },
 		{ "readonly",          'r',  POPT_ARG_NONE, &opt_readonly,              0, N_("Create a readonly mapping"), NULL },
 		{ "batch-mode",        'q',  POPT_ARG_NONE, &opt_batch_mode,            0, N_("Do not ask for confirmation"), NULL },
 		{ "timeout",           't',  POPT_ARG_INT, &opt_timeout,                0, N_("Timeout for interactive passphrase prompt (in seconds)"), N_("secs") },
@@ -3606,25 +3679,29 @@ int main(int argc, const char **argv)
 
 	while((r = poptGetNextOpt(popt_context)) > 0) {
 		unsigned long long ull_value;
-		char *endp;
+		char *endp, *str = poptGetOptArg(popt_context);
 
 		if (r == 6) {
-			const char *kf = poptGetOptArg(popt_context);
-			if (tools_is_stdin(kf))
-				opt_keyfile_stdin = kf;
-			else if (opt_keyfiles_count < MAX_KEYFILES)
-				opt_keyfiles[opt_keyfiles_count++] = kf;
+			free(opt_key_file);
+			opt_key_file = str;
+			if (tools_is_stdin(str)) {
+				free(opt_keyfile_stdin);
+				opt_keyfile_stdin = strdup(str);
+			} else if (opt_keyfiles_count < MAX_KEYFILES)
+				opt_keyfiles[opt_keyfiles_count++] = strdup(str);
 			total_keyfiles++;
 			continue;
 		}
 
 		errno = 0;
-		ull_value = strtoull(popt_tmp, &endp, 0);
-		if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
+		ull_value = strtoull(str, &endp, 0);
+		if (*endp || !*str || !isdigit(*str) ||
 		    (errno == ERANGE && ull_value == ULLONG_MAX) ||
 		    (errno != 0 && ull_value == 0))
 			r = POPT_ERROR_BADNUMBER;
 
+		free(str);
+
 		switch(r) {
 			case 1:
 				opt_size = ull_value;
@@ -3675,26 +3752,26 @@ int main(int argc, const char **argv)
 			action_argv[1] = tmp;
 		}
 		aname = "open";
-		opt_type = "plain";
+		device_type = "plain";
 	} else if (!strcmp(aname, "plainOpen")) {
 		aname = "open";
-		opt_type = "plain";
+		device_type = "plain";
 	} else if (!strcmp(aname, "luksOpen")) {
 		aname = "open";
-		opt_type = "luks";
+		device_type = "luks";
 	} else if (!strcmp(aname, "loopaesOpen")) {
 		aname = "open";
-		opt_type = "loopaes";
+		device_type = "loopaes";
 	} else if (!strcmp(aname, "tcryptOpen")) {
 		aname = "open";
-		opt_type = "tcrypt";
+		device_type = "tcrypt";
 	} else if (!strcmp(aname, "bitlkOpen")) {
 		aname = "open";
-		opt_type = "bitlk";
+		device_type = "bitlk";
 	} else if (!strcmp(aname, "tcryptDump")) {
-		opt_type = "tcrypt";
+		device_type = "tcrypt";
 	} else if (!strcmp(aname, "bitlkDump")) {
-		opt_type = "bitlk";
+		device_type = "bitlk";
 	} else if (!strcmp(aname, "remove") ||
 		   !strcmp(aname, "plainClose") ||
 		   !strcmp(aname, "luksClose") ||
@@ -3704,18 +3781,19 @@ int main(int argc, const char **argv)
 		aname = "close";
 	} else if (!strcmp(aname, "luksErase")) {
 		aname = "erase";
-		opt_type = "luks";
+		device_type = "luks";
 	} else if (!strcmp(aname, "luksConfig")) {
 		aname = "config";
-		opt_type = "luks2";
+		device_type = "luks2";
 	} else if (!strcmp(aname, "refresh")) {
 		aname = "open";
 		opt_refresh = 1;
-	}
+	} else if (opt_type)
+		device_type = opt_type;
 
 	/* ignore user supplied type and query device type instead */
 	if (opt_refresh)
-		opt_type = NULL;
+		device_type = NULL;
 
 	for(action = action_types; action->type; action++)
 		if (strcmp(action->type, aname) == 0)
@@ -3740,7 +3818,7 @@ int main(int argc, const char **argv)
 		      _("Option --deferred is allowed only for close command."),
 		      poptGetInvocationName(popt_context));
 
-	if (opt_shared && (strcmp(aname, "open") || strcmp_or_null(opt_type, "plain")))
+	if (opt_shared && (strcmp(aname, "open") || strcmp_or_null(device_type, "plain")))
 		usage(popt_context, EXIT_FAILURE,
 		      _("Option --shared is allowed only for open of plain device."),
 		      poptGetInvocationName(popt_context));
@@ -3792,8 +3870,8 @@ int main(int argc, const char **argv)
 		      _("Options --label and --subsystem are allowed only for luksFormat and config LUKS2 operations."),
 		      poptGetInvocationName(popt_context));
 
-	if (opt_test_passphrase && (strcmp(aname, "open") || !opt_type ||
-	    (strncmp(opt_type, "luks", 4) && strcmp(opt_type, "tcrypt") && strcmp(opt_type, "bitlk"))))
+	if (opt_test_passphrase && (strcmp(aname, "open") || !device_type ||
+	    (strncmp(device_type, "luks", 4) && strcmp(device_type, "tcrypt") && strcmp(device_type, "bitlk"))))
 		usage(popt_context, EXIT_FAILURE,
 		      _("Option --test-passphrase is allowed only for open of LUKS, TCRYPT and BITLK devices."),
 		      poptGetInvocationName(popt_context));
@@ -3815,7 +3893,7 @@ int main(int argc, const char **argv)
 		if (opt_key_file)
 			log_err(_("Option --key-file takes precedence over specified key file argument."));
 		else
-			opt_key_file = action_argv[1];
+			opt_key_file = strdup(action_argv[1]);
 	}
 
 	if (opt_keyfile_size < 0 || opt_new_keyfile_size < 0 || opt_key_size < 0)
@@ -3823,7 +3901,7 @@ int main(int argc, const char **argv)
 		      _("Negative number for option not permitted."),
 		      poptGetInvocationName(popt_context));
 
-	if (total_keyfiles > 1 && (strcmp_or_null(opt_type, "tcrypt")))
+	if (total_keyfiles > 1 && (strcmp_or_null(device_type, "tcrypt")))
 		usage(popt_context, EXIT_FAILURE, _("Only one --key-file argument is allowed."),
 		      poptGetInvocationName(popt_context));
 
@@ -3861,20 +3939,20 @@ int main(int argc, const char **argv)
 		      poptGetInvocationName(popt_context));
 
 	if (opt_skip && (strcmp(aname, "open") ||
-	    (strcmp_or_null(opt_type, "plain") && strcmp(opt_type, "loopaes"))))
+	    (strcmp_or_null(device_type, "plain") && strcmp(device_type, "loopaes"))))
 		usage(popt_context, EXIT_FAILURE,
 		_("Option --skip is supported only for open of plain and loopaes devices."),
 		poptGetInvocationName(popt_context));
 
 	if (opt_offset && ((strcmp(aname, "reencrypt") && strcmp(aname, "open") && strcmp(aname, "luksFormat")) ||
-	    (!strcmp(aname, "open") && strcmp_or_null(opt_type, "plain") && strcmp(opt_type, "loopaes")) ||
-	    (!strcmp(aname, "luksFormat") && opt_type && strncmp(opt_type, "luks", 4))))
+	    (!strcmp(aname, "open") && strcmp_or_null(device_type, "plain") && strcmp(device_type, "loopaes")) ||
+	    (!strcmp(aname, "luksFormat") && device_type && strncmp(device_type, "luks", 4))))
 		usage(popt_context, EXIT_FAILURE,
 		_("Option --offset is supported only for open of plain and loopaes devices, luksFormat and device reencryption."),
 		poptGetInvocationName(popt_context));
 
 	if ((opt_tcrypt_hidden || opt_tcrypt_system || opt_tcrypt_backup) && strcmp(aname, "tcryptDump") &&
-	    (strcmp(aname, "open") || !opt_type || strcmp(opt_type, "tcrypt")))
+	    (strcmp(aname, "open") || !device_type || strcmp(device_type, "tcrypt")))
 		usage(popt_context, EXIT_FAILURE,
 		_("Option --tcrypt-hidden, --tcrypt-system or --tcrypt-backup is supported only for TCRYPT device."),
 		poptGetInvocationName(popt_context));
@@ -3884,7 +3962,7 @@ int main(int argc, const char **argv)
 		_("Option --tcrypt-hidden cannot be combined with --allow-discards."),
 		poptGetInvocationName(popt_context));
 
-	if (opt_veracrypt && (!opt_type || strcmp(opt_type, "tcrypt")))
+	if (opt_veracrypt && (!device_type || strcmp(device_type, "tcrypt")))
 		usage(popt_context, EXIT_FAILURE,
 		_("Option --veracrypt is supported only for TCRYPT device type."),
 		poptGetInvocationName(popt_context));
@@ -3923,7 +4001,7 @@ int main(int argc, const char **argv)
 		_("Keyslot specification is required."),
 		poptGetInvocationName(popt_context));
 
-	if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf))
+	if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &set_pbkdf))
 		usage(popt_context, EXIT_FAILURE,
 		_("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id."),
 		poptGetInvocationName(popt_context));
@@ -3934,7 +4012,7 @@ int main(int argc, const char **argv)
 		poptGetInvocationName(popt_context));
 
 	if (opt_sector_size && strcmp(aname, "reencrypt") && strcmp(aname, "luksFormat") &&
-	    (strcmp(aname, "open") || strcmp_or_null(opt_type, "plain")))
+	    (strcmp(aname, "open") || strcmp_or_null(device_type, "plain")))
 		usage(popt_context, EXIT_FAILURE,
 		      _("Sector size option is not supported for this command."),
 		      poptGetInvocationName(popt_context));
@@ -3980,6 +4058,7 @@ int main(int argc, const char **argv)
 
 	if (opt_disable_locks && crypt_metadata_locking(NULL, 0)) {
 		log_std(_("Cannot disable metadata locking."));
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_FAILURE);
 	}
@@ -4037,6 +4116,7 @@ int main(int argc, const char **argv)
 		      poptGetInvocationName(popt_context));
 
 	r = run_action(action);
+	tools_cleanup();
 	poptFreeContext(popt_context);
 	return r;
 }
diff --git a/src/cryptsetup.h b/src/cryptsetup.h
index 1afcf433..66446308 100644
--- a/src/cryptsetup.h
+++ b/src/cryptsetup.h
@@ -24,6 +24,7 @@
 #ifndef CRYPTSETUP_H
 #define CRYPTSETUP_H
 
+#include <stdbool.h>
 #include <string.h>
 #include <stdlib.h>
 #include <stdarg.h>
@@ -97,7 +98,6 @@ int tools_get_key(const char *prompt,
 void tools_passphrase_msg(int r);
 int tools_is_stdin(const char *key_file);
 int tools_string_to_size(struct crypt_device *cd, const char *s, uint64_t *size);
-int tools_is_cipher_null(const char *cipher);
 
 void tools_clear_line(void);
 
@@ -116,6 +116,11 @@ int tools_wipe_all_signatures(const char *path);
 int tools_lookup_crypt_device(struct crypt_device *cd, const char *type,
 		const char *data_device_path, char *name, size_t name_length);
 
+/* each utility is required to implement it */
+void tools_cleanup(void);
+
+#define FREE_AND_NULL(x) do { free(x); x = NULL; } while (0)
+
 /* Log */
 #define log_dbg(x...) clogger(NULL, CRYPT_LOG_DEBUG, __FILE__, __LINE__, x)
 #define log_std(x...) clogger(NULL, CRYPT_LOG_NORMAL, __FILE__, __LINE__, x)
diff --git a/src/cryptsetup_reencrypt.c b/src/cryptsetup_reencrypt.c
index a536093b..e5f9786c 100644
--- a/src/cryptsetup_reencrypt.c
+++ b/src/cryptsetup_reencrypt.c
@@ -29,16 +29,25 @@
 
 #define NO_UUID "cafecafe-cafe-cafe-cafe-cafecafeeeee"
 
-static const char *opt_cipher = NULL;
-static const char *opt_hash = NULL;
-static const char *opt_key_file = NULL;
-static const char *opt_master_key_file = NULL;
-static const char *opt_uuid = NULL;
-static const char *opt_type = "luks";
+static char *opt_cipher = NULL;
+static char *opt_hash = NULL;
+static char *opt_key_file = NULL;
+static char *opt_master_key_file = NULL;
+static char *opt_uuid = NULL;
+static char *opt_type = NULL;
+static char *opt_pbkdf = NULL;
+static char *opt_header_device = NULL;
+
+/* helper strings converted to uint64_t later */
+static char *opt_reduce_size_str = NULL;
+static char *opt_device_size_str = NULL;
+
+static uint64_t opt_reduce_size = 0;
+static uint64_t opt_device_size = 0;
+
 static long opt_keyfile_size = 0;
 static long opt_keyfile_offset = 0;
 static int opt_iteration_time = 0;
-static const char *opt_pbkdf = NULL;
 static long opt_pbkdf_memory = DEFAULT_LUKS2_MEMORY_KB;
 static long opt_pbkdf_parallel = DEFAULT_LUKS2_PARALLEL_THREADS;
 static long opt_pbkdf_iterations = 0;
@@ -54,16 +63,11 @@ static int opt_key_size = 0;
 static int opt_new = 0;
 static int opt_keep_key = 0;
 static int opt_decrypt = 0;
-static const char *opt_header_device = NULL;
-
-static const char *opt_reduce_size_str = NULL;
-static uint64_t opt_reduce_size = 0;
-
-static const char *opt_device_size_str = NULL;
-static uint64_t opt_device_size = 0;
 
 static const char **action_argv;
 
+static const char *set_pbkdf = NULL;
+
 #define MAX_SLOT 32
 #define MAX_TOKEN 32
 struct reenc_ctx {
@@ -113,6 +117,20 @@ typedef enum {
 	CHECK_OPEN,
 } header_magic;
 
+void tools_cleanup(void)
+{
+	FREE_AND_NULL(opt_cipher);
+	FREE_AND_NULL(opt_hash);
+	FREE_AND_NULL(opt_key_file);
+	FREE_AND_NULL(opt_master_key_file);
+	FREE_AND_NULL(opt_uuid);
+	FREE_AND_NULL(opt_type);
+	FREE_AND_NULL(opt_pbkdf);
+	FREE_AND_NULL(opt_header_device);
+	FREE_AND_NULL(opt_reduce_size_str);
+	FREE_AND_NULL(opt_device_size_str);
+}
+
 static void _quiet_log(int level, const char *msg, void *usrptr)
 {
 	if (!opt_debug)
@@ -482,7 +500,7 @@ static int set_pbkdf_params(struct crypt_device *cd, const char *dev_type)
 	if (!pbkdf_default)
 		return -EINVAL;
 
-	pbkdf.type = opt_pbkdf ?: pbkdf_default->type;
+	pbkdf.type = set_pbkdf ?: pbkdf_default->type;
 	pbkdf.hash = opt_hash ?: pbkdf_default->hash;
 	pbkdf.time_ms = (uint32_t)opt_iteration_time ?: pbkdf_default->time_ms;
 	if (strcmp(pbkdf.type, CRYPT_KDF_PBKDF2)) {
@@ -1590,10 +1608,12 @@ static void help(poptContext popt_context,
 	if (key->shortName == '?') {
 		log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION);
 		poptPrintHelp(popt_context, stdout, 0);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else if (key->shortName == 'V') {
 		log_std("%s %s\n", PACKAGE_REENC, PACKAGE_VERSION);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else
@@ -1688,7 +1708,7 @@ int main(int argc, const char **argv)
 		      poptGetInvocationName(popt_context));
 	}
 
-	if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &opt_pbkdf))
+	if (opt_pbkdf && crypt_parse_pbkdf(opt_pbkdf, &set_pbkdf))
 		usage(popt_context, EXIT_FAILURE,
 		_("Password-based key derivation function (PBKDF) can be only pbkdf2 or argon2i/argon2id."),
 		poptGetInvocationName(popt_context));
@@ -1764,8 +1784,7 @@ int main(int argc, const char **argv)
 	}
 
 	r = run_reencrypt(action_argv[0]);
-
+	tools_cleanup();
 	poptFreeContext(popt_context);
-
 	return translate_errno(r);
 }
diff --git a/src/integritysetup.c b/src/integritysetup.c
index 51778da9..26d5a285 100644
--- a/src/integritysetup.c
+++ b/src/integritysetup.c
@@ -27,8 +27,19 @@
 #define DEFAULT_ALG_NAME "crc32c"
 #define MAX_KEY_SIZE 4096
 
-static const char *opt_journal_size_str = NULL;
+static char *opt_data_device = NULL;
+static char *opt_integrity = NULL; /* DEFAULT_ALG_NAME */
+static char *opt_integrity_key_file = NULL;
+static char *opt_journal_integrity = NULL; /* none */
+static char *opt_journal_integrity_key_file = NULL;
+static char *opt_journal_crypt = NULL; /* none */
+static char *opt_journal_crypt_key_file = NULL;
+
+/* helper strings converted to uint64_t later */
+static char *opt_journal_size_str = NULL;
+
 static uint64_t opt_journal_size = 0;
+
 static int opt_interleave_sectors = 0;
 static int opt_journal_watermark = 0;
 static int opt_bitmap_sectors_per_bit = 0;
@@ -37,34 +48,35 @@ static int opt_bitmap_flush_time = 0;
 static int opt_tag_size = 0;
 static int opt_sector_size = 0;
 static int opt_buffer_sectors = 0;
-
 static int opt_no_wipe = 0;
-
-static const char *opt_data_device = NULL;
-
-static const char *opt_integrity = DEFAULT_ALG_NAME;
-static const char *opt_integrity_key_file = NULL;
 static int opt_integrity_key_size = 0;
-
-static const char *opt_journal_integrity = NULL; /* none */
-static const char *opt_journal_integrity_key_file = NULL;
 static int opt_journal_integrity_key_size = 0;
-
-static const char *opt_journal_crypt = NULL; /* none */
-static const char *opt_journal_crypt_key_file = NULL;
 static int opt_journal_crypt_key_size = 0;
-
 static int opt_integrity_nojournal = 0;
 static int opt_integrity_recovery = 0;
 static int opt_integrity_bitmap = 0;
 static int opt_integrity_legacy_padding = 0;
-
+static int opt_integrity_legacy_hmac = 0;
+static int opt_integrity_legacy_recalculate = 0;
 static int opt_integrity_recalculate = 0;
 static int opt_allow_discards = 0;
 
+static const char *integrity_alg = DEFAULT_ALG_NAME;
 static const char **action_argv;
 static int action_argc;
 
+void tools_cleanup(void)
+{
+	FREE_AND_NULL(opt_data_device);
+	FREE_AND_NULL(opt_integrity);
+	FREE_AND_NULL(opt_integrity_key_file);
+	FREE_AND_NULL(opt_journal_integrity);
+	FREE_AND_NULL(opt_journal_integrity_key_file);
+	FREE_AND_NULL(opt_journal_crypt);
+	FREE_AND_NULL(opt_journal_crypt_key_file);
+	FREE_AND_NULL(opt_journal_size_str);
+}
+
 // FIXME: move this to tools and handle EINTR
 static int _read_mk(const char *file, char **key, int keysize)
 {
@@ -189,14 +201,12 @@ static int action_format(int arg)
 	int r;
 	size_t signatures;
 
-	if (opt_integrity) {
-		r = crypt_parse_hash_integrity_mode(opt_integrity, integrity);
+	r = crypt_parse_hash_integrity_mode(integrity_alg, integrity);
 	if (r < 0) {
 		log_err(_("No known integrity specification pattern detected."));
 		return r;
 	}
 	params.integrity = integrity;
-	}
 
 	if (opt_journal_integrity) {
 		r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity);
@@ -246,6 +256,9 @@ static int action_format(int arg)
 	if (opt_integrity_legacy_padding)
 		crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_PADDING);
 
+	if (opt_integrity_legacy_hmac)
+		crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_HMAC);
+
 	r = crypt_format(cd, CRYPT_INTEGRITY, NULL, NULL, NULL, NULL, 0, &params);
 	if (r < 0) /* FIXME: call wipe signatures again */
 		goto out;
@@ -278,14 +291,12 @@ static int action_open(int arg)
 	char *integrity_key = NULL;
 	int r;
 
-	if (opt_integrity) {
-		r = crypt_parse_hash_integrity_mode(opt_integrity, integrity);
+	r = crypt_parse_hash_integrity_mode(integrity_alg, integrity);
 	if (r < 0) {
 		log_err(_("No known integrity specification pattern detected."));
 		return r;
 	}
 	params.integrity = integrity;
-	}
 
 	if (opt_journal_integrity) {
 		r = crypt_parse_hash_integrity_mode(opt_journal_integrity, journal_integrity);
@@ -313,7 +324,7 @@ static int action_open(int arg)
 	if (opt_integrity_bitmap)
 		activate_flags |= CRYPT_ACTIVATE_NO_JOURNAL_BITMAP;
 
-	if (opt_integrity_recalculate)
+	if (opt_integrity_recalculate || opt_integrity_legacy_recalculate)
 		activate_flags |= CRYPT_ACTIVATE_RECALCULATE;
 	if (opt_allow_discards)
 		activate_flags |= CRYPT_ACTIVATE_ALLOW_DISCARDS;
@@ -329,6 +340,9 @@ static int action_open(int arg)
 	if (r)
 		goto out;
 
+	if (opt_integrity_legacy_recalculate)
+		crypt_set_compatibility(cd, CRYPT_COMPAT_LEGACY_INTEGRITY_RECALC);
+
 	r = crypt_activate_by_volume_key(cd, action_argv[1], integrity_key,
 					 opt_integrity_key_size, activate_flags);
 out:
@@ -506,10 +520,12 @@ static void help(poptContext popt_context,
 
 		log_std(_("\nDefault compiled-in dm-integrity parameters:\n"
 			  "\tChecksum algorithm: %s\n"), DEFAULT_ALG_NAME);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else if (key->shortName == 'V') {
 		log_std("%s %s\n", PACKAGE_INTEGRITY, PACKAGE_VERSION);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else
@@ -576,6 +592,9 @@ int main(int argc, const char **argv)
 		{ "integrity-recalculate",     '\0', POPT_ARG_NONE,  &opt_integrity_recalculate,  0, N_("Recalculate initial tags automatically."), NULL },
 		{ "integrity-legacy-padding",  '\0', POPT_ARG_NONE,  &opt_integrity_legacy_padding, 0, N_("Use inefficient legacy padding (old kernels)"), NULL },
 
+		{ "integrity-legacy-hmac",     '\0', POPT_ARG_NONE,  &opt_integrity_legacy_hmac, 0, N_("Do not protect superblock with HMAC (old kernels)"), NULL },
+		{ "integrity-legacy-recalculate",'\0',POPT_ARG_NONE, &opt_integrity_legacy_recalculate, 0, N_("Allow recalculating of volumes with HMAC keys (old kernels)"), NULL },
+
 		{ "allow-discards",            '\0', POPT_ARG_NONE,  &opt_allow_discards, 0, N_("Allow discards (aka TRIM) requests for device"), NULL },
 		POPT_TABLEEND
 	};
@@ -629,6 +648,9 @@ int main(int argc, const char **argv)
 		aname = "close";
 	}
 
+	if (opt_integrity)
+		integrity_alg = opt_integrity;
+
 	for (action = action_types; action->type; action++)
 		if (strcmp(action->type, aname) == 0)
 			break;
@@ -679,9 +701,6 @@ int main(int argc, const char **argv)
 	   (!opt_integrity_key_file && opt_integrity_key_size))
 		usage(popt_context, EXIT_FAILURE, _("Both key file and key size options must be specified."),
 		      poptGetInvocationName(popt_context));
-	if (!opt_integrity && opt_integrity_key_file)
-		usage(popt_context, EXIT_FAILURE, _("Integrity algorithm must be specified if integrity key is used."),
-		      poptGetInvocationName(popt_context));
 
 	if ((opt_journal_integrity_key_file && !opt_journal_integrity_key_size) ||
 	   (!opt_journal_integrity_key_file && opt_journal_integrity_key_size))
@@ -718,6 +737,7 @@ int main(int argc, const char **argv)
 	}
 
 	r = run_action(action);
+	tools_cleanup();
 	poptFreeContext(popt_context);
 	return r;
 }
diff --git a/src/utils_password.c b/src/utils_password.c
index 55c1343f..dce73fa1 100644
--- a/src/utils_password.c
+++ b/src/utils_password.c
@@ -62,51 +62,86 @@ static int tools_check_pwquality(const char *password)
 #elif defined ENABLE_PASSWDQC
 #include <passwdqc.h>
 
-static int tools_check_pwquality(const char *password)
+static int tools_check_passwdqc(const char *password)
 {
 	passwdqc_params_t params;
-	char *parse_reason;
+	char *parse_reason = NULL;
 	const char *check_reason;
 	const char *config = PASSWDQC_CONFIG_FILE;
+	int r = -EINVAL;
 
 	passwdqc_params_reset(&params);
 
 	if (*config && passwdqc_params_load(&params, &parse_reason, config)) {
 		log_err(_("Cannot check password quality: %s"),
 			(parse_reason ? parse_reason : "Out of memory"));
-		free(parse_reason);
-		return -EINVAL;
+		goto out;
 	}
 
 	check_reason = passwdqc_check(&params.qc, password, NULL, NULL);
 	if (check_reason) {
 		log_err(_("Password quality check failed: Bad passphrase (%s)"),
 			check_reason);
-		return -EPERM;
-	}
-
-	return 0;
+		r = -EPERM;
+	} else
+		r = 0;
+out:
+#if HAVE_PASSWDQC_PARAMS_FREE
+	passwdqc_params_free(&params);
+#endif
+	free(parse_reason);
+	return r;
 }
-#else /* !(ENABLE_PWQUALITY || ENABLE_PASSWDQC) */
-static int tools_check_pwquality(const char *password)
+#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */
+
+/* coverity[ +tainted_string_sanitize_content : arg-0 ] */
+static int tools_check_password(const char *password)
 {
+#if defined ENABLE_PWQUALITY
+	return tools_check_pwquality(password);
+#elif defined ENABLE_PASSWDQC
+	return tools_check_passwdqc(password);
+#else
 	return 0;
+#endif
 }
-#endif /* ENABLE_PWQUALITY || ENABLE_PASSWDQC */
 
 /* Password reading helpers */
+
+static ssize_t read_tty_eol(int fd, char *pass, size_t maxlen)
+{
+	bool eol = false;
+	size_t read_size = 0;
+	ssize_t r;
+
+	do {
+		r = read(fd, pass, maxlen - read_size);
+		if ((r == -1 && errno != EINTR) || quit)
+			return -1;
+		if (r >= 0) {
+			if (!r || pass[r-1] == '\n')
+				eol = true;
+			read_size += (size_t)r;
+			pass = pass + r;
+		}
+	} while (!eol && read_size != maxlen);
+
+	return (ssize_t)read_size;
+}
+
+/* The pass buffer is zeroed and has trailing \0 already " */
 static int untimed_read(int fd, char *pass, size_t maxlen)
 {
 	ssize_t i;
 
-	i = read(fd, pass, maxlen);
+	i = read_tty_eol(fd, pass, maxlen);
 	if (i > 0) {
+		if (pass[i-1] == '\n')
 			pass[i-1] = '\0';
 		i = 0;
-	} else if (i == 0) { /* EOF */
-		*pass = 0;
+	} else if (i == 0) /* empty input */
 		i = -1;
-	}
+
 	return i;
 }
 
@@ -193,10 +228,9 @@ static int crypt_get_key_tty(const char *prompt,
 		log_err(_("Error reading passphrase from terminal."));
 		goto out_err;
 	}
-	pass[key_size_max] = '\0';
 
 	if (verify) {
-		pass_verify = crypt_safe_alloc(key_size_max);
+		pass_verify = crypt_safe_alloc(key_size_max + 1);
 		if (!pass_verify) {
 			log_err(_("Out of memory while reading passphrase."));
 			r = -ENOMEM;
@@ -276,7 +310,7 @@ int tools_get_key(const char *prompt,
 
 	/* Check pwquality for password (not keyfile) */
 	if (pwquality && !opt_force_password && !key_file && !r)
-		r = tools_check_pwquality(*key);
+		r = tools_check_password(*key);
 
 	return r;
 }
@@ -293,7 +327,7 @@ int tools_read_mk(const char *file, char **key, int keysize)
 {
 	int fd;
 
-	if (!keysize || !key)
+	if (keysize <= 0 || !key)
 		return -EINVAL;
 
 	*key = crypt_safe_alloc(keysize);
@@ -323,6 +357,9 @@ int tools_write_mk(const char *file, const char *key, int keysize)
 {
 	int fd, r = -EINVAL;
 
+	if (keysize <= 0 || !key)
+		return -EINVAL;
+
 	fd = open(file, O_CREAT|O_EXCL|O_WRONLY, S_IRUSR);
 	if (fd < 0) {
 		log_err(_("Cannot open keyfile %s for write."), file);
diff --git a/src/utils_tools.c b/src/utils_tools.c
index 47bcfe12..32ff0b73 100644
--- a/src/utils_tools.c
+++ b/src/utils_tools.c
@@ -238,6 +238,7 @@ __attribute__ ((noreturn)) void usage(poptContext popt_context,
 	poptPrintUsage(popt_context, stderr, 0);
 	if (error)
 		log_err("%s: %s", more, error);
+	tools_cleanup();
 	poptFreeContext(popt_context);
 	exit(exitcode);
 }
@@ -596,14 +597,6 @@ out:
 	return r;
 }
 
-int tools_is_cipher_null(const char *cipher)
-{
-	if (!cipher)
-		return 0;
-
-	return !strcmp(cipher, "cipher_null") ? 1 : 0;
-}
-
 /*
  * Keyfile - is standard input treated as a binary file (no EOL handling).
  */
diff --git a/src/veritysetup.c b/src/veritysetup.c
index fcc12a6b..92b8c5ea 100644
--- a/src/veritysetup.c
+++ b/src/veritysetup.c
@@ -23,29 +23,38 @@
 
 #define PACKAGE_VERITY "veritysetup"
 
-static int use_superblock = 1;
-
-static const char *fec_device = NULL;
-static int fec_roots = DEFAULT_VERITY_FEC_ROOTS;
-static const char *hash_algorithm = NULL;
-static int hash_type = 1;
-static int data_block_size = DEFAULT_VERITY_DATA_BLOCK;
-static int hash_block_size = DEFAULT_VERITY_HASH_BLOCK;
+static char *opt_fec_device = NULL;
+static char *opt_hash_algorithm = NULL;
+static char *opt_salt = NULL;
+static char *opt_uuid = NULL;
+static char *opt_root_hash_signature = NULL;
+
+static int opt_use_superblock = 1;
+static int opt_fec_roots = DEFAULT_VERITY_FEC_ROOTS;
+static int opt_hash_type = 1;
+static int opt_data_block_size = DEFAULT_VERITY_DATA_BLOCK;
+static int opt_hash_block_size = DEFAULT_VERITY_HASH_BLOCK;
 static uint64_t data_blocks = 0;
-static const char *salt_string = NULL;
 static uint64_t hash_offset = 0;
 static uint64_t fec_offset = 0;
-static const char *opt_uuid = NULL;
 static int opt_restart_on_corruption = 0;
 static int opt_panic_on_corruption = 0;
 static int opt_ignore_corruption = 0;
 static int opt_ignore_zero_blocks = 0;
 static int opt_check_at_most_once = 0;
-static const char *opt_root_hash_signature = NULL;
 
 static const char **action_argv;
 static int action_argc;
 
+void tools_cleanup(void)
+{
+	FREE_AND_NULL(opt_fec_device);
+	FREE_AND_NULL(opt_hash_algorithm);
+	FREE_AND_NULL(opt_salt);
+	FREE_AND_NULL(opt_uuid);
+	FREE_AND_NULL(opt_root_hash_signature);
+}
+
 static int _prepare_format(struct crypt_params_verity *params,
 			   const char *data_device,
 			   uint32_t flags)
@@ -53,16 +62,16 @@ static int _prepare_format(struct crypt_params_verity *params,
 	char *salt = NULL;
 	int len;
 
-	params->hash_name = hash_algorithm ?: DEFAULT_VERITY_HASH;
+	params->hash_name = opt_hash_algorithm ?: DEFAULT_VERITY_HASH;
 	params->data_device = data_device;
-	params->fec_device = fec_device;
-	params->fec_roots = fec_roots;
+	params->fec_device = opt_fec_device;
+	params->fec_roots = opt_fec_roots;
 
-	if (salt_string && !strcmp(salt_string, "-")) {
+	if (opt_salt && !strcmp(opt_salt, "-")) {
 		params->salt_size = 0;
 		params->salt = NULL;
-	} else if (salt_string) {
-		len = crypt_hex_to_bytes(salt_string, &salt, 0);
+	} else if (opt_salt) {
+		len = crypt_hex_to_bytes(opt_salt, &salt, 0);
 		if (len < 0) {
 			log_err(_("Invalid salt string specified."));
 			return -EINVAL;
@@ -74,12 +83,12 @@ static int _prepare_format(struct crypt_params_verity *params,
 		params->salt = NULL;
 	}
 
-	params->data_block_size = data_block_size;
-	params->hash_block_size = hash_block_size;
+	params->data_block_size = opt_data_block_size;
+	params->hash_block_size = opt_hash_block_size;
 	params->data_size = data_blocks;
 	params->hash_area_offset = hash_offset;
 	params->fec_area_offset = fec_offset;
-	params->hash_type = hash_type;
+	params->hash_type = opt_hash_type;
 	params->flags = flags;
 
 	return 0;
@@ -102,13 +111,13 @@ static int action_format(int arg)
 		close(r);
 	}
 	/* Try to create FEC image if doesn't exist */
-	if (fec_device) {
-		r = open(fec_device, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
+	if (opt_fec_device) {
+		r = open(opt_fec_device, O_WRONLY | O_EXCL | O_CREAT, S_IRUSR | S_IWUSR);
 		if (r < 0 && errno != EEXIST) {
-			log_err(_("Cannot create FEC image %s for writing."), fec_device);
+			log_err(_("Cannot create FEC image %s for writing."), opt_fec_device);
 			return -EINVAL;
 		} else if (r >= 0) {
-			log_dbg("Created FEC image %s.", fec_device);
+			log_dbg("Created FEC image %s.", opt_fec_device);
 			close(r);
 		}
 	}
@@ -116,7 +125,7 @@ static int action_format(int arg)
 	if ((r = crypt_init(&cd, action_argv[1])))
 		goto out;
 
-	if (!use_superblock)
+	if (!opt_use_superblock)
 		flags |= CRYPT_VERITY_NO_HEADER;
 
 	r = _prepare_format(&params, action_argv[0], flags);
@@ -161,12 +170,12 @@ static int _activate(const char *dm_device,
 	if (opt_check_at_most_once)
 		activate_flags |= CRYPT_ACTIVATE_CHECK_AT_MOST_ONCE;
 
-	if (use_superblock) {
+	if (opt_use_superblock) {
 		params.flags = flags;
 		params.hash_area_offset = hash_offset;
 		params.fec_area_offset = fec_offset;
-		params.fec_device = fec_device;
-		params.fec_roots = fec_roots;
+		params.fec_device = opt_fec_device;
+		params.fec_roots = opt_fec_roots;
 		r = crypt_load(cd, CRYPT_VERITY, &params);
 	} else {
 		r = _prepare_format(&params, data_device, flags | CRYPT_VERITY_NO_HEADER);
@@ -329,7 +338,7 @@ static int action_status(int arg)
 
 		if (vp.fec_device) {
 			log_std("  FEC device:  %s\n", vp.fec_device);
-			if ((backing_file = crypt_loop_backing_file(vp.fec_device))) {
+			if ((backing_file = crypt_loop_backing_file(opt_fec_device))) {
 				log_std("  FEC loop:    %s\n", backing_file);
 				free(backing_file);
 			}
@@ -431,10 +440,12 @@ static void help(poptContext popt_context,
 			DEFAULT_VERITY_HASH, DEFAULT_VERITY_DATA_BLOCK,
 			DEFAULT_VERITY_HASH_BLOCK, DEFAULT_VERITY_SALT_SIZE,
 			1);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else if (key->shortName == 'V') {
 		log_std("%s %s\n", PACKAGE_VERITY, PACKAGE_VERSION);
+		tools_cleanup();
 		poptFreeContext(popt_context);
 		exit(EXIT_SUCCESS);
 	} else
@@ -455,7 +466,6 @@ static int run_action(struct action_type *action)
 
 int main(int argc, const char **argv)
 {
-	static char *popt_tmp;
 	static const char *null_action_argv[] = {NULL};
 	static struct poptOption popt_help_options[] = {
 		{ NULL,    '\0', POPT_ARG_CALLBACK, help, 0, NULL,                         NULL },
@@ -468,17 +478,17 @@ int main(int argc, const char **argv)
 		{ NULL,              '\0', POPT_ARG_INCLUDE_TABLE, popt_help_options, 0, N_("Help options:"), NULL },
 		{ "verbose",         'v',  POPT_ARG_NONE, &opt_verbose,      0, N_("Shows more detailed error messages"), NULL },
 		{ "debug",           '\0', POPT_ARG_NONE, &opt_debug,        0, N_("Show debug messages"), NULL },
-		{ "no-superblock",   0,    POPT_ARG_VAL,  &use_superblock,   0, N_("Do not use verity superblock"), NULL },
-		{ "format",          0,    POPT_ARG_INT,  &hash_type,        0, N_("Format type (1 - normal, 0 - original Chrome OS)"), N_("number") },
-		{ "data-block-size", 0,    POPT_ARG_INT,  &data_block_size,  0, N_("Block size on the data device"), N_("bytes") },
-		{ "hash-block-size", 0,    POPT_ARG_INT,  &hash_block_size,  0, N_("Block size on the hash device"), N_("bytes") },
-		{ "fec-roots",       0,    POPT_ARG_INT,  &fec_roots,        0, N_("FEC parity bytes"), N_("bytes") },
-		{ "data-blocks",     0,    POPT_ARG_STRING, &popt_tmp,       1, N_("The number of blocks in the data file"), N_("blocks") },
-		{ "fec-device",      0,    POPT_ARG_STRING, &fec_device,     0, N_("Path to device with error correction data"), N_("path") },
-		{ "hash-offset",     0,    POPT_ARG_STRING, &popt_tmp,       2, N_("Starting offset on the hash device"), N_("bytes") },
-		{ "fec-offset",      0,    POPT_ARG_STRING, &popt_tmp,       3, N_("Starting offset on the FEC device"), N_("bytes") },
-		{ "hash",            'h',  POPT_ARG_STRING, &hash_algorithm, 0, N_("Hash algorithm"), N_("string") },
-		{ "salt",            's',  POPT_ARG_STRING, &salt_string,    0, N_("Salt"), N_("hex string") },
+		{ "no-superblock",   0,    POPT_ARG_VAL,  &opt_use_superblock,   0, N_("Do not use verity superblock"), NULL },
+		{ "format",          0,    POPT_ARG_INT,  &opt_hash_type,        0, N_("Format type (1 - normal, 0 - original Chrome OS)"), N_("number") },
+		{ "data-block-size", 0,    POPT_ARG_INT,  &opt_data_block_size,  0, N_("Block size on the data device"), N_("bytes") },
+		{ "hash-block-size", 0,    POPT_ARG_INT,  &opt_hash_block_size,  0, N_("Block size on the hash device"), N_("bytes") },
+		{ "fec-roots",       0,    POPT_ARG_INT,  &opt_fec_roots,        0, N_("FEC parity bytes"), N_("bytes") },
+		{ "data-blocks",     0,    POPT_ARG_STRING, NULL,       1, N_("The number of blocks in the data file"), N_("blocks") },
+		{ "fec-device",      0,    POPT_ARG_STRING, &opt_fec_device,     0, N_("Path to device with error correction data"), N_("path") },
+		{ "hash-offset",     0,    POPT_ARG_STRING, NULL,       2, N_("Starting offset on the hash device"), N_("bytes") },
+		{ "fec-offset",      0,    POPT_ARG_STRING, NULL,       3, N_("Starting offset on the FEC device"), N_("bytes") },
+		{ "hash",            'h',  POPT_ARG_STRING, &opt_hash_algorithm, 0, N_("Hash algorithm"), N_("string") },
+		{ "salt",            's',  POPT_ARG_STRING, &opt_salt,    0, N_("Salt"), N_("hex string") },
 		{ "uuid",            '\0', POPT_ARG_STRING, &opt_uuid,       0, N_("UUID for device to use"), NULL },
 		{ "root-hash-signature",'\0', POPT_ARG_STRING, &opt_root_hash_signature,  0, N_("Path to root hash signature file"), NULL },
 		{ "restart-on-corruption", 0,POPT_ARG_NONE,&opt_restart_on_corruption, 0, N_("Restart kernel if corruption is detected"), NULL },
@@ -506,15 +516,17 @@ int main(int argc, const char **argv)
 
 	while((r = poptGetNextOpt(popt_context)) > 0) {
 		unsigned long long ull_value;
-		char *endp;
+		char *endp, *str = poptGetOptArg(popt_context);
 
 		errno = 0;
-		ull_value = strtoull(popt_tmp, &endp, 10);
-		if (*endp || !*popt_tmp || !isdigit(*popt_tmp) ||
+		ull_value = strtoull(str, &endp, 10);
+		if (*endp || !*str || !isdigit(*str) ||
 		    (errno == ERANGE && ull_value == ULLONG_MAX) ||
 		    (errno != 0 && ull_value == 0))
 			r = POPT_ERROR_BADNUMBER;
 
+		free(str);
+
 		switch(r) {
 			case 1:
 				data_blocks = ull_value;
@@ -577,7 +589,7 @@ int main(int argc, const char **argv)
 		      poptGetInvocationName(popt_context));
 	}
 
-	if (data_block_size < 0 || hash_block_size < 0 || hash_type < 0) {
+	if (opt_data_block_size < 0 || opt_hash_block_size < 0 || opt_hash_type < 0) {
 		usage(popt_context, EXIT_FAILURE,
 		      _("Negative number for option not permitted."),
 		      poptGetInvocationName(popt_context));
@@ -610,6 +622,7 @@ int main(int argc, const char **argv)
 	}
 
 	r = run_action(action);
+	tools_cleanup();
 	poptFreeContext(popt_context);
 	return r;
 }

Attachment: signature.asc
Description: PGP signature


Reply to: