int crypt_keyslot_change_by_passphrase(struct crypt_device *cd,
	int keyslot_old,
	int keyslot_new,
	const char *passphrase,
	size_t passphrase_size,
	const char *new_passphrase,
	size_t new_passphrase_size)
{
	int digest = -1, r, keyslot_new_orig = keyslot_new;
	struct luks2_keyslot_params params;
	struct volume_key *vk = NULL;

	if (!passphrase || !new_passphrase)
		return -EINVAL;

	log_dbg(cd, "Changing passphrase from old keyslot %d to new %d.",
		keyslot_old, keyslot_new);

	if ((r = onlyLUKS(cd)))
		return r;

	if (isLUKS1(cd->type))
		r = LUKS_open_key_with_hdr(keyslot_old, passphrase, passphrase_size,
					   &cd->u.luks1.hdr, &vk, cd);
	else if (isLUKS2(cd->type)) {
		r = LUKS2_keyslot_open(cd, keyslot_old, CRYPT_ANY_SEGMENT, passphrase, passphrase_size, &vk);
		/* will fail for keyslots w/o digest. fix if supported in a future */
		if (r >= 0) {
			digest = LUKS2_digest_by_keyslot(&cd->u.luks2.hdr, r);
			if (digest < 0)
				r = -EINVAL;
		}
	} else
		r = -EINVAL;
	if (r < 0)
		goto out;

	if (keyslot_old != CRYPT_ANY_SLOT && keyslot_old != r) {
		log_dbg(cd, "Keyslot mismatch.");
		goto out;
	}
	keyslot_old = r;

	if (keyslot_new == CRYPT_ANY_SLOT) {
		if (isLUKS1(cd->type))
			keyslot_new = LUKS_keyslot_find_empty(&cd->u.luks1.hdr);
		else if (isLUKS2(cd->type))
			keyslot_new = LUKS2_keyslot_find_empty(cd, &cd->u.luks2.hdr, vk->keylength);
		if (keyslot_new < 0)
			keyslot_new = keyslot_old;
	}
	log_dbg(cd, "Key change, old slot %d, new slot %d.", keyslot_old, keyslot_new);

	if (isLUKS1(cd->type)) {
		if (keyslot_old == keyslot_new) {
			log_dbg(cd, "Key slot %d is going to be overwritten.", keyslot_old);
			(void)crypt_keyslot_destroy(cd, keyslot_old);
		}
		r = LUKS_set_key(keyslot_new, new_passphrase, new_passphrase_size,
				 &cd->u.luks1.hdr, vk, cd);
	} else if (isLUKS2(cd->type)) {
		r = LUKS2_keyslot_params_default(cd, &cd->u.luks2.hdr, &params);
		if (r)
			goto out;

		if (keyslot_old != keyslot_new) {
			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 */
			r = LUKS2_keyslot_wipe(cd, &cd->u.luks2.hdr, keyslot_old, 1);
			if (r) {
				/* (void)crypt_keyslot_destroy(cd, keyslot_old); */
				r = -EINVAL;
				goto out;
			}
		}

		r = LUKS2_keyslot_store(cd,  &cd->u.luks2.hdr,
					keyslot_new, new_passphrase,
					new_passphrase_size, vk, &params);
		if (r < 0)
			goto out;

		/* Swap old & new so the final keyslot number remains */
		if (keyslot_new_orig == CRYPT_ANY_SLOT && keyslot_old != keyslot_new) {
			r = LUKS2_keyslot_swap(cd, &cd->u.luks2.hdr, keyslot_old, keyslot_new);
			if (r < 0)
				goto out;

			/* Swap slot id */
			r = keyslot_old;
			keyslot_old = keyslot_new;
			keyslot_new = r;
		}
	} else
		r = -EINVAL;

	if (r >= 0 && keyslot_old != keyslot_new)
		r = crypt_keyslot_destroy(cd, keyslot_old);

	if (r < 0)
		log_err(cd, _("Failed to swap new key slot."));
out:
	crypt_free_volume_key(vk);
	if (r < 0) {
		_luks2_reload(cd);
		return r;
	}
	return keyslot_new;
}