Subject: [PATCH] [FEAT SEC1800] zkey: Add keystore implementation
From: Philipp Rudo <prudo@linux.ibm.com>

Summary:     zkey: Add support of protected key crypto for dm-crypt.
Description: Support the usage of protected key crypto for dm-crypt disks in
             plain format by providing a tool to manage a key repository
             allowing to associate secure keys with disk partitions or logical
             volumes.
Upstream-ID: c944f23d7e1f983499b4c5fcf04430dc49902f04
Problem-ID:  SEC1800

Upstream-Description:

             zkey: Add keystore implementation

             Add a keystore implementation that stores secure AES keys in a
             key repository, located in a directory, e.g. '/etc/zkey/repository'.
             The keystore allows you to generate, validate, re-encipher, modify,
             list, delete, etc secure keys.

             Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
             Reviewed-by: Hendrik Brueckner <brueckner@linux.ibm.com>
             Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>


Signed-off-by: Philipp Rudo <prudo@linux.ibm.com>
---
 zkey/Makefile   |    3 
 zkey/keystore.c | 3299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 zkey/keystore.h |   77 +
 3 files changed, 3378 insertions(+), 1 deletion(-)

--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -27,8 +27,9 @@ libs = $(rootdir)/libutil/libutil.a
 zkey.o: zkey.c pkey.h misc.h
 pkey.o: pkey.c pkey.h
 properties.o: properties.c properties.h
+keystore.o: keystore.c keystore.h properties.h
 
-zkey: zkey.o pkey.o properties.o $(libs)
+zkey: zkey.o pkey.o properties.o keystore.o $(libs)
 
 install: all
 	$(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR)
--- /dev/null
+++ b/zkey/keystore.c
@@ -0,0 +1,3299 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Keystore handling functions
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * s390-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#include <argz.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fnmatch.h>
+#include <regex.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "lib/util_base.h"
+#include "lib/util_file.h"
+#include "lib/util_libc.h"
+#include "lib/util_panic.h"
+#include "lib/util_path.h"
+#include "lib/util_rec.h"
+
+#include "keystore.h"
+#include "pkey.h"
+#include "properties.h"
+
+struct key_filenames {
+	char *skey_filename;
+	char *info_filename;
+	char *renc_filename;
+};
+
+#define FILE_EXTENSION_LEN	5
+#define SKEY_FILE_EXTENSION	".skey"
+#define INFO_FILE_EXTENSION	".info"
+#define RENC_FILE_EXTENSION	".renc"
+
+#define LOCK_FILE_NAME		".lock"
+
+#define PROP_NAME_KEY_TYPE	"key-type"
+#define PROP_NAME_CIPHER	"cipher"
+#define PROP_NAME_IV_MODE	"iv-mode"
+#define PROP_NAME_DESCRIPTION	"description"
+#define PROP_NAME_VOLUMES	"volumes"
+#define PROP_NAME_APQNS		"apqns"
+#define PROP_NAME_SECTOR_SIZE	"sector-size"
+#define PROP_NAME_CREATION_TIME	"creation-time"
+#define PROP_NAME_CHANGE_TIME	"update-time"
+#define PROP_NAME_REENC_TIME	"reencipher-time"
+
+#define IS_XTS(secure_key_size) (secure_key_size > SECURE_KEY_SIZE ? 1 : 0)
+
+#define REC_KEY			"Key"
+#define REC_DESCRIPTION		"Description"
+#define REC_SEC_KEY_SIZE	"Secure key size"
+#define REC_CLR_KEY_SIZE	"Clear key size"
+#define REC_XTS			"XTS type key"
+#define REC_VOLUMES		"Volumes"
+#define REC_APQNS		"APQNs"
+#define REC_KEY_FILE		"Key file name"
+#define REC_SECTOR_SIZE		"Sector size"
+#define REC_STATUS		"Status"
+#define REC_MASTERKEY		"Encrypted with"
+#define REC_CREATION_TIME	"Created"
+#define REC_CHANGE_TIME		"Changed"
+#define REC_REENC_TIME		"Re-enciphered"
+
+#define pr_verbose(keystore, fmt...)	do {				\
+						if (keystore->verbose)	\
+							warnx(fmt);	\
+					} while (0)
+
+/**
+ * Gets the file names of the .skey and .info and .renc files for a named
+ * key in the key strore's directory
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name        the name of the key
+ * @param[out] names      is filled with the names of the files
+ *
+ * @returns 0 for success or a negative errno in case of an error*
+ */
+static int _keystore_get_key_filenames(struct keystore *keystore,
+				      const char *name,
+				      struct key_filenames *names)
+{
+	if (strpbrk(name, "/\\ *?'\"")) {
+		warnx("Key name '%s' contains invalid characters", name);
+		return -EINVAL;
+	}
+
+	util_asprintf(&names->skey_filename, "%s/%s%s", keystore->directory,
+		      name, SKEY_FILE_EXTENSION);
+	util_asprintf(&names->info_filename, "%s/%s%s", keystore->directory,
+		      name, INFO_FILE_EXTENSION);
+	util_asprintf(&names->renc_filename, "%s/%s%s", keystore->directory,
+			      name, RENC_FILE_EXTENSION);
+
+	pr_verbose(keystore, "File names for key '%s': '%s' and '%s'", name,
+		   names->skey_filename, names->info_filename);
+	return 0;
+}
+
+/**
+ * Checks if the .renc file exists.
+ *
+ * @param[in] file_names  names of the files
+ *
+ * @returns 1 if the file exist, 0 if the file do not exist
+ */
+static int _keystore_reencipher_key_exists(struct key_filenames *file_names)
+{
+	struct stat sb;
+	int rc;
+
+	rc = stat(file_names->renc_filename, &sb);
+	if (rc == 0 && !S_ISREG(sb.st_mode))
+		rc = 1;
+
+	return !rc;
+}
+
+/**
+ * Checks if both, the .skey and the .info (and .renc) files exist.
+ *
+ * @param[in] file_names  names of the files
+ *
+ * @returns 1 if all files exist, 0 if all files do not exist, -1 if one
+ *          file exists but other one does not exist (inconsistent state)
+ */
+static int _keystore_exists_keyfiles(struct key_filenames *file_names)
+{
+	struct stat sb_skey, sb_info;
+	int rc_skey, rc_info;
+
+	rc_skey = stat(file_names->skey_filename, &sb_skey);
+	if (rc_skey == 0 && !S_ISREG(sb_skey.st_mode))
+		rc_skey = 1;
+
+	rc_info = stat(file_names->info_filename, &sb_info);
+	if (rc_info == 0 && !S_ISREG(sb_info.st_mode))
+		rc_info = 1;
+
+	if (rc_skey == 0 && rc_info == 0)
+		return 1;
+	if (rc_skey != 0 && rc_info != 0 &&
+	    _keystore_reencipher_key_exists(file_names) == 0)
+		return 0;
+	return -1;
+}
+
+/**
+ * Checks if the files belonging to a key exist. If not an appropriate error
+ * message is issued.
+ *
+ * @param[in] file_names names of the files
+ * @param[in] name       name of the key
+ *
+ * @returns 0 if the files exist, -ENOENT if the files do not exist, -EPERM if
+ *          one file exists but the other does not exist (inconsistent state)
+ */
+static int _keystore_ensure_keyfiles_exist(struct key_filenames *file_names,
+					   const char *name)
+{
+	int rc;
+
+	rc = _keystore_exists_keyfiles(file_names);
+	if (rc == 0) {
+		warnx("Key '%s' does not exist", name);
+		return -ENOENT;
+	}
+	if (rc == -1) {
+		warnx("Key '%s' is in an inconsistent state", name);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * Checks if the files belonging to a key do not exist. If they files exist,
+ * an appropriate error message is issued.
+ *
+ * @param[in] file_names names of the files
+ * @param[in] name       name of the key
+ *
+ * @returns 0 if the files exist, -EEXIST if the files exist already, -EPERM if
+ *          one file exists but the other does not exist (inconsistent state)
+ */
+static int _keystore_ensure_keyfiles_not_exist(struct key_filenames *file_names,
+					       const char *name)
+{
+	int rc;
+
+	rc = _keystore_exists_keyfiles(file_names);
+	if (rc == 1) {
+		warnx("Key '%s' exists already", name);
+		return -EEXIST;
+	}
+	if (rc == -1) {
+		warnx("Key '%s' is in an inconsistent state", name);
+		return -EPERM;
+	}
+
+	return 0;
+}
+
+/**
+ * Frees the file names stored inside the struct key_filenames
+ *
+ * @param[in] names      names of the files
+ */
+static void _keystore_free_key_filenames(struct key_filenames *names)
+{
+	if (names->skey_filename)
+		free(names->skey_filename);
+	if (names->info_filename)
+		free(names->info_filename);
+	if (names->renc_filename)
+		free(names->renc_filename);
+}
+
+/**
+ * Sets the file permissions of the file to the permissions and the group
+ * of the repository directory
+ *
+ * @param[in] keystroe     the keystore
+ * @param[in] filename     the name of the file to set permissions for
+ *
+ * @returns 0 on success, or a negative errno value on failure
+ */
+static int _keystore_set_file_permission(struct keystore *keystore,
+					 const char *filename)
+{
+	int rc;
+
+	if (chmod(filename, keystore->mode) != 0) {
+		rc = -errno;
+		warnx("chmod faild on file '%s': %s", filename, strerror(-rc));
+		return rc;
+	}
+
+	if (chown(filename, geteuid(), keystore->owner) != 0) {
+		rc = -errno;
+		warnx("chown faild on file '%s': %s", filename, strerror(-rc));
+		return rc;
+	}
+
+	return 0;
+}
+
+/**
+ *  Checks if the sector size is power of two and in range 512 - 4096 bytes.
+ *
+ * @param[in] sector_size   the sector size
+ *
+ * @returns 1 if the sector size is valid, 0 otherwise
+ */
+static int _keystore_valid_sector_size(size_t sector_size)
+{
+	if (sector_size == 0)
+		return 1;
+	if (sector_size < 512 || sector_size > 4096)
+		return 0;
+	if (sector_size & (sector_size - 1))
+		return 0;
+	return 1;
+}
+
+typedef int (*check_association_t)(const char *value, bool remove,
+				   char **normalized, void *private);
+
+/**
+ * Set an association property. For each object set function check_func is
+ * called (if not NULL).
+ *
+ * @param[in/out] key_props  the properties object to modify
+ * @param[in] property       the name of the property to modify
+ * @param[in] newvalue       the new value(s) to add, remove or set
+ * @param[in] msg_obj        the name of the object for error messages
+ * @param[in] check_func     a function to call on each object before it is
+ *                           added, removed or set to the property
+ * @param[in] check_private  a private pointer passed to check_func
+ *
+ * @returns 0 for success, or a negative errno value in case of an error, or
+ *          whatever check_func returns if check_func returns a non-zero value.
+ */
+static int _keystore_set_association(struct properties *key_props,
+				     const char *property,
+				     const char *newvalue,
+				     const char *msg_obj,
+				     check_association_t check_func,
+				     void *check_private)
+{
+	char *normalized = NULL;
+	char **newvals = NULL;
+	char *value = NULL;
+	char *changedval;
+	char *newval;
+	int i, rc = 0;
+
+	newvals = str_list_split(newvalue);
+	if (newvals == NULL)
+		return -EINVAL;
+
+	for (i = 0; newvals[i] != NULL; i++) {
+		if (check_func != NULL) {
+			rc = check_func(newvals[i], 0, &normalized,
+					check_private);
+			if (rc != 0)
+				goto out;
+		}
+
+		newval = normalized != NULL ? normalized : newvals[i];
+		if (value == NULL)
+			changedval = str_list_add("", newval);
+		else
+			changedval = str_list_add(value, newval);
+		if (changedval == NULL) {
+			warnx("The %s '%s' is already specified or contains "
+			      "invalid characters", msg_obj, newval);
+			rc = -EEXIST;
+			goto out;
+		}
+		if (normalized != NULL)
+			free(normalized);
+		normalized = NULL;
+		free(value);
+		value = changedval;
+	}
+
+	rc = properties_set(key_props, property, value != NULL ? value : "");
+	if (rc != 0)
+		warnx("Invalid characters in %ss", msg_obj);
+
+out:
+	if (newvals != NULL)
+		str_list_free_string_array(newvals);
+	if (value != NULL)
+		free(value);
+	if (normalized != NULL)
+		free(normalized);
+	return rc;
+}
+
+/**
+ * Add a value to an association property. For each object added function
+ * check_func is called (if not NULL).
+ *
+ * @param[in/out] key_props  the properties object to modify
+ * @param[in] property       the name of the property to modify
+ * @param[in] newvalue       the new value(s) to add, remove or set
+ * @param[in] msg_obj        the name of the object for error messages
+ * @param[in] check_func     a function to call on each object before it is
+ *                           added, removed or set to the property
+ * @param[in] check_private  a private pointer passed to check_func
+ *
+ * @returns 0 for success, or a negative errno value in case of an error, or
+ *          whatever check_func returns if check_func returns a non-zero value.
+ */
+static int _keystore_add_association(struct properties *key_props,
+				     const char *property,
+				     const char *newvalue,
+				     const char *msg_obj,
+				     check_association_t check_func,
+				     void *check_private)
+{
+	char *normalized = NULL;
+	char **newvals = NULL;
+	char *changedval;
+	char *newval;
+	int i, rc = 0;
+	char *value;
+
+	value = properties_get(key_props, property);
+	if (value == NULL)
+		return _keystore_set_association(key_props, property,
+						 newvalue, msg_obj,
+						 check_func, check_private);
+
+	newvals = str_list_split(newvalue);
+	if (newvals == NULL) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; newvals[i] != NULL; i++) {
+		if (check_func != NULL) {
+			rc = check_func(newvals[i], 0, &normalized,
+					check_private);
+			if (rc != 0)
+				goto out;
+		}
+
+		newval = normalized != NULL ? normalized : newvals[i];
+		changedval = str_list_add(value, newval);
+		if (changedval == NULL) {
+			warnx("The %s '%s' is already associated with this key "
+			      "or contains invalid characters", msg_obj,
+			      newval);
+			rc = -EEXIST;
+			goto out;
+		}
+		if (normalized != NULL)
+			free(normalized);
+		normalized = NULL;
+		free(value);
+		value = changedval;
+	}
+
+	rc = properties_set(key_props, property, value);
+	if (rc != 0)
+		warnx("Invalid characters in %ss", msg_obj);
+
+out:
+	if (newvals != NULL)
+		str_list_free_string_array(newvals);
+	if (value != NULL)
+		free(value);
+	if (normalized != NULL)
+		free(normalized);
+	return rc;
+}
+
+/**
+ * Removes a value from an association property. For each object removed
+ * function check_func is called (if not NULL).
+ *
+ * @param[in/out] key_props  the properties object to modify
+ * @param[in] property       the name of the property to modify
+ * @param[in] delvalue       the value(s) to remove
+ * @param[in] msg_obj        the name of the object for error messages
+ * @param[in] check_func     a function to call on each object before it is
+ *                           added, removed or set to the property
+ * @param[in] check_private  a private pointer passed to check_func
+ *
+ * @returns 0 for success, or a negative errno value in case of an error, or
+ *          whatever check_func returns if check_func returns a non-zero value.
+ */
+static int _keystore_remove_association(struct properties *key_props,
+					const char *property,
+					const char *delvalue,
+					const char *msg_obj,
+					check_association_t check_func,
+					void *check_private)
+{
+	char *normalized = NULL;
+	char **delvals = NULL;
+	char *changedval;
+	char *delval;
+	int i, rc = 0;
+	char *value;
+
+	value = properties_get(key_props, property);
+	if (value == NULL) {
+		warnx("No %ss are currently associated with this key", msg_obj);
+		return -ENOENT;
+	}
+
+	delvals = str_list_split(delvalue);
+	if (delvals == NULL) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	for (i = 0; delvals[i] != NULL; i++) {
+		if (check_func != NULL) {
+			rc = check_func(delvals[i], 1, &normalized,
+					check_private);
+			if (rc != 0)
+				goto out;
+		}
+
+		delval = normalized != NULL ? normalized : delvals[i];
+		changedval = str_list_remove(value, delval);
+		if (changedval == NULL) {
+			warnx("%s '%s' is not associated with this key",
+			      msg_obj, delval);
+			rc = -ENOENT;
+			goto out;
+		}
+		if (normalized != NULL)
+			free(normalized);
+		normalized = NULL;
+		free(value);
+		value = changedval;
+	}
+
+	rc = properties_set(key_props, property, value);
+	if (rc != 0)
+		warnx("Invalid characters in %ss", msg_obj);
+
+out:
+	if (delvals != NULL)
+		str_list_free_string_array(delvals);
+	if (value != NULL)
+		free(value);
+	if (normalized != NULL)
+		free(normalized);
+	return rc;
+}
+
+/**
+ * Change an association property. This function adds the objects in the
+ * comma separated string when newvalue begins with a '+'. It removes the
+ * objects when newvalue begins with a '-', or it sets the property to
+ * newvalue when newvalue does not begin with '+' or '-'. For each object
+ * added, Removed or set function check_func is called (if not NULL).
+ *
+ * @param[in/out] key_props  the properties object to modify
+ * @param[in] property       the name of the property to modify
+ * @param[in] newvalue       the new value(s) to add, remove or set
+ * @param[in] msg_obj        the name of the object for error messages
+ * @param[in] check_func     a function to call on each object before it is
+ *                           added, removed or set to the property
+ * @param[in] check_private  a private pointer passed to check_func
+ *
+ * @returns 0 for success, or a negative errno value in case of an error, or
+ *          whatever check_func returns if check_func returns a non-zero value.
+ */
+static int _keystore_change_association(struct properties *key_props,
+					const char *property,
+					const char *newvalue,
+					const char *msg_obj,
+					check_association_t check_func,
+					void *check_private)
+{
+	switch (*newvalue) {
+	case '+':
+		return _keystore_add_association(key_props, property,
+						 &newvalue[1], msg_obj,
+						 check_func, check_private);
+	case '-':
+		return _keystore_remove_association(key_props, property,
+						    &newvalue[1], msg_obj,
+						    check_func, check_private);
+	default:
+		return _keystore_set_association(key_props, property,
+						 newvalue, msg_obj,
+						 check_func, check_private);
+
+	}
+}
+
+/**
+ * Filter match function for APQNs
+ *
+ * @param[in] pattern    the pattern to match
+ * @param[in] apqn       the apqn to match
+ * @param[in] flags      Not used here
+ *
+ * @returns Zero if string matches pattern, FNM_NOMATCH if there is no match
+ *          or another nonzero value if there is an error.
+ */
+static int _keystore_apqn_match(const char *pattern, const char *apqn,
+				int UNUSED(flags))
+{
+	char *modified;
+	char *pattern_domain;
+	char *pattern_card;
+	char *copy;
+	int card, domain;
+	size_t i;
+	char *ch;
+	int rc;
+
+	if (sscanf(pattern, "%x.%x", &card, &domain) == 2) {
+		util_asprintf(&modified, "%02x.%04x", card, domain);
+		goto match;
+	}
+
+	copy = util_strdup(pattern);
+
+	ch = strchr(copy, '.');
+	if (ch != NULL) {
+		*ch = '\0';
+		pattern_card = copy;
+		pattern_domain = ch + 1;
+
+		modified = NULL;
+		if (strchr(pattern_card, '*') == NULL &&
+		    strlen(pattern_card) < 2) {
+			for (i = 0; i < 2 - strlen(pattern_card); i++)
+				modified = util_strcat_realloc(modified, "0");
+		}
+		modified = util_strcat_realloc(modified, pattern_card);
+
+		modified = util_strcat_realloc(modified, ".");
+
+		if (strchr(pattern_domain, '*') == NULL &&
+		    strlen(pattern_domain) < 4) {
+			for (i = 0; i < 4 - strlen(pattern_domain); i++)
+				modified = util_strcat_realloc(modified, "0");
+		}
+		modified = util_strcat_realloc(modified, pattern_domain);
+	} else {
+		modified = util_strdup(copy);
+	}
+	free(copy);
+
+match:
+	rc = fnmatch(modified, apqn, FNM_CASEFOLD);
+
+	free(modified);
+	return rc;
+}
+
+typedef int (*filter_match_t)(const char *pattern, const char *string,
+			      int flags);
+
+/*
+ * Checks if the value matches the filter list. The value can be a comma
+ * separated string.
+ *
+ * If the filter values contain a second part separated by a colon (':'), then
+ * the filter matches only if both parts match. If the filter values do not
+ * contain a second part,then only the first part is checked, and the second
+ * parts of the values are ignored.
+ *
+ * @param[in] value     the value to check
+ * @param[in] filter_list a list of filter strings to match the value with
+ * @param[in] match_func the filter match function. If NULL fnmatch() is used.
+ *
+ * @returns 1 for a match, 0 for not matched
+ */
+static int _keystore_match_filter(const char *value,
+				  char **filter_list,
+				  filter_match_t match_func)
+{
+	char **value_list;
+	int i, k, rc = 0;
+	char *ch;
+
+	if (filter_list == NULL)
+		return 1;
+
+	if (match_func == NULL)
+		match_func = fnmatch;
+
+	value_list = str_list_split(value);
+	for (i = 0; filter_list[i] != NULL && rc == 0; i++) {
+		for (k = 0; value_list[k] != NULL; k++) {
+			/*
+			 * Ignore part after ':' of value if filter does
+			 * not also contain a ':' part.
+			 */
+			if (strchr(filter_list[i], ':') == NULL) {
+				ch = strchr(value_list[k], ':');
+				if (ch != NULL)
+					*ch = '\0';
+			}
+
+			if (match_func(filter_list[i], value_list[k], 0) == 0) {
+				rc = 1;
+				break;
+			}
+		}
+	}
+
+	str_list_free_string_array(value_list);
+	return rc;
+}
+
+/*
+ * Checks if the property value matches the filter list. The property value
+ * can be a comma separated string.
+ *
+ * If the filter values contain a second part separated by a colon (':'), then
+ * the filter matches only if both parts match. If the filter values do not
+ * contain a second part,then only the first part is checked, and the second
+ * parts of the values are ignored.
+ *
+ * @param[in] properties   a properties object
+ * @param[in] property     the name of the property to check
+ * @param[in] filter_list  a list of filter strings to match the value with
+ * @param[in] match_func the filter match function. If NULL fnmatch() is used.
+ *
+ * @returns 1 for a match, 0 for not matched
+ */
+static int _keystore_match_filter_property(struct properties *properties,
+					   const char *property,
+					   char **filter_list,
+					   filter_match_t match_func)
+{
+	char *value;
+	int rc;
+
+	if (filter_list == NULL)
+		return 1;
+
+	value = properties_get(properties, property);
+	if (value == NULL)
+		return 0;
+
+	rc = _keystore_match_filter(value, filter_list, match_func);
+
+	free(value);
+	return rc;
+}
+
+/**
+ * Checks if a key name matches a name filter
+ *
+ * @param[in] name         the name to check
+ * @param[in] name_filter  the name filter to match against
+ *
+ * @returns 1 if the filter matches, 0 otherwise
+ */
+static int _keystore_match_name_filter(const char *name,
+				       const char *name_filter)
+{
+	if (name_filter == NULL)
+		return 1;
+
+	if (fnmatch(name_filter, name, 0) != 0)
+		return 0;
+
+	return 1;
+}
+
+/**
+ * Filters directory entries for scanfile(). Only entries that are regular
+ * files and who's name ends with '.info' are matched.
+ */
+static int _keystore_info_file_filter(const struct dirent *dirent)
+{
+	size_t len;
+
+	if (dirent->d_type != DT_REG)
+		return 0;
+
+	len = strlen(dirent->d_name);
+	if (len > FILE_EXTENSION_LEN &&
+	    strcmp(&dirent->d_name[len - FILE_EXTENSION_LEN],
+		   INFO_FILE_EXTENSION) == 0)
+		return 1;
+
+	return 0;
+}
+
+typedef int (*process_key_t)(struct keystore *keystore,
+			     const char *name, struct properties *properties,
+			     struct key_filenames *file_names, void *private);
+
+/**
+ * Iterates over all keys stored in the keystore. For every key that matches
+ * the specified filter process_func is called.
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name_filter    the name filter. Can contain wild cards.
+ *                           NULL means no name filter.
+ * @param[in] volume_filter  the volume filter. Can contain wild cards, and
+ *                           mutliple volume filters separated by commas.
+ *                           If the filter does not contain the ':dm-name' part,
+ *                           then the volumes are matched without the dm-name
+ *                           part. If the filter contains the ':dm-name' part,
+ *                           then the filter is matched including the dm-name
+ *                           part.
+ *                           NULL means no volume filter.
+ *                           specification is ignored for filter matching.
+ * @param[in] apqn_filter    the APQN filter. Can contain wild cards, and
+ *                           mutliple APQN filters separated by commas.
+ *                           NULL means no APQN filter.
+ * @param[in] process_func   the callback function called for a matching key
+ * @param[in/out] process_private private data passed to the process_func
+ *
+ * @returns 0 for success, or a negative errno value in case of an error, or
+ *          whatever process_func returns if process_func returns a non-zero
+ *          value.
+ */
+static int _keystore_process_filtered(struct keystore *keystore,
+				      const char *name_filter,
+				      const char *volume_filter,
+				      const char *apqn_filter,
+				      process_key_t process_func,
+				      void *process_private)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	char **apqn_filter_list = NULL;
+	char **vol_filter_list = NULL;
+	struct properties *key_props;
+	struct dirent **namelist;
+	int n, i, rc = 0;
+	bool skip = 0;
+	char *name;
+	int len;
+
+	pr_verbose(keystore, "Process_filtered: name_filter = '%s', "
+		   "volume_filter = '%s', apqn_filter = '%s'", name_filter,
+		   volume_filter, apqn_filter);
+
+	if (volume_filter != NULL)
+		vol_filter_list = str_list_split(volume_filter);
+	if (apqn_filter != NULL)
+		apqn_filter_list = str_list_split(apqn_filter);
+
+	n = scandir(keystore->directory, &namelist, _keystore_info_file_filter,
+		    alphasort);
+	if (n == -1) {
+		rc = -errno;
+		pr_verbose(keystore, "scandir failed with: %s", strerror(-rc));
+		return rc;
+	}
+
+	for (i = 0; i < n ; i++) {
+		if (skip)
+			goto free;
+
+		name = namelist[i]->d_name;
+		len = strlen(name);
+		if (len > FILE_EXTENSION_LEN)
+			name[len - FILE_EXTENSION_LEN] = '\0';
+
+		if (_keystore_match_name_filter(name, name_filter) == 0) {
+			pr_verbose(keystore,
+				   "Key '%s' filtered out due to name filter",
+				   name);
+			goto free;
+		}
+
+		rc = _keystore_get_key_filenames(keystore, name, &file_names);
+		if (rc != 0)
+			goto free;
+
+		rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+		if (rc != 0)
+			goto free_names;
+
+		key_props = properties_new();
+		rc = properties_load(key_props, file_names.info_filename, 1);
+		if (rc != 0) {
+			warnx("Key '%s' does not exist or is invalid", name);
+			goto free_prop;
+		}
+
+		rc = _keystore_match_filter_property(key_props,
+						     PROP_NAME_VOLUMES,
+						     vol_filter_list, NULL);
+		if (rc == 0) {
+			pr_verbose(keystore,
+				   "Key '%s' filtered out due to volumes filter",
+				   name);
+			goto free_prop;
+		}
+
+		rc = _keystore_match_filter_property(key_props,
+						     PROP_NAME_APQNS,
+						     apqn_filter_list,
+						     _keystore_apqn_match);
+		if (rc == 0) {
+			pr_verbose(keystore,
+				   "Key '%s' filtered out due to APQN filter",
+				   name);
+			goto free_prop;
+		}
+
+		rc = process_func(keystore, name, key_props, &file_names,
+				  process_private);
+		if (rc != 0) {
+			pr_verbose(keystore, "Process function returned %d",
+				   rc);
+			skip = 1;
+		}
+
+free_prop:
+		properties_free(key_props);
+free_names:
+		_keystore_free_key_filenames(&file_names);
+free:
+		free(namelist[i]);
+	}
+	free(namelist);
+
+	if (vol_filter_list)
+		str_list_free_string_array(vol_filter_list);
+	if (apqn_filter_list)
+		str_list_free_string_array(apqn_filter_list);
+
+	pr_verbose(keystore, "Process_filtered rc = %d", rc);
+	return rc;
+}
+
+/**
+ * Checks if the specified APQN is of type CCA and is online
+ *
+ * @param[in] card      card number
+ * @param[in] domain    the domain
+ *
+ * @returns 1 if its a CCA card and is online, 0 if offline and -1 if its
+ *          not a CCA card.
+ */
+static int _keystore_is_apqn_online(int card, int domain)
+{
+	long int online;
+	char *dev_path;
+	char type[20];
+	int rc = 1;
+
+	dev_path = util_path_sysfs("bus/ap/devices/card%02x", card);
+	if (!util_path_is_dir(dev_path)) {
+		rc = 0;
+		goto out;
+	}
+	if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
+		rc = 0;
+		goto out;
+	}
+	if (online == 0) {
+		rc = 0;
+		goto out;
+	}
+	if (util_file_read_line(type, sizeof(type), "%s/type", dev_path) != 0) {
+		rc = 0;
+		goto out;
+	}
+	if (strncmp(type, "CEX", 3) != 0 || strlen(type) < 5) {
+		rc = 0;
+		goto out;
+	}
+	if (type[4] != 'C') {
+		rc = -1;
+		goto out;
+	}
+	free(dev_path);
+
+	dev_path = util_path_sysfs("bus/ap/devices/card%02x/%02x.%04x", card,
+				   card, domain);
+	if (!util_path_is_dir(dev_path)) {
+		rc = 0;
+		goto out;
+	}
+	if (util_file_read_l(&online, 10, "%s/online", dev_path) != 0) {
+		rc = 0;
+		goto out;
+	}
+	if (online == 0) {
+		rc = 0;
+		goto out;
+	}
+
+out:
+	free(dev_path);
+	return rc;
+}
+
+/**
+ * Checks an APQN value for its syntax. This is a callback function for
+ * function _keystore_change_association().
+ *
+ * @param[in] apqn     the APQN value to check
+ * @param[in] remove   if true the apqn is removed
+ * @param[out] normalized normalized value on return or NULL if no change
+ * @param[in] private  private data (not used here)
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+static int _keystore_apqn_check(const char *apqn, bool remove,
+				char **normalized, void *UNUSED(private))
+{
+	int rc, card, domain;
+	regmatch_t pmatch[1];
+	regex_t reg_buf;
+
+	*normalized = NULL;
+
+	rc = regcomp(&reg_buf, "[[:xdigit:]]+\\.[[:xdigit:]]", REG_EXTENDED);
+	if (rc != 0)
+		return -EIO;
+
+	rc = regexec(&reg_buf, apqn, (size_t) 1, pmatch, 0);
+	if (rc != 0) {
+		warnx("the APQN '%s' is not valid", apqn);
+		return -EINVAL;
+	}
+
+	if (sscanf(apqn, "%x.%x", &card, &domain) != 2)
+		return -EINVAL;
+
+	util_asprintf(normalized, "%02x.%04x", card, domain);
+
+	if (remove)
+		return 0;
+
+	rc = _keystore_is_apqn_online(card, domain);
+	if (rc != 1) {
+		warnx("The APQN %02x.%04x is %s", card, domain,
+		      rc == -1 ? "not a CCA card" : "not online");
+		return -EIO;
+	}
+
+	return 0;
+}
+
+
+struct volume_check {
+	struct keystore *keystore;
+	const char *name;
+	const char *volume;
+};
+
+/**
+ * Processing callback function for the volume association check function.
+ *
+ * @param[in] keystore   the keystore (not used here)
+ * @param[in] name       the name of the key
+ * @param[in] properties the properties object of the key (not used here)
+ * @param[in] file_names the file names used by this key (not used here)
+ * @param[in] private    private data: struct volume_check
+ *
+ * @returns 0 if the key name is equal to the key we are checking the volume
+ *          associations for, -EINVAL otherwise (i.e. to indicate duplicate
+ *          volume association)
+ */
+static int _keystore_volume_check_process(struct keystore *UNUSED(keystore),
+					  const char *name,
+					  struct properties *UNUSED(properties),
+					  struct key_filenames
+							  *UNUSED(file_names),
+					  void *private)
+{
+	struct volume_check *info = (struct volume_check *)private;
+
+	warnx("Key '%s' is already associated with volume '%s'", name,
+	      info->volume);
+	return -EINVAL;
+}
+
+/**
+ * Checks if the volume is a block device
+ *
+ * @param[in] volume the volume to check
+ *
+ * @return 1 if the volume is a block device, 0 otherwise
+ */
+static int _keystore_is_block_device(const char *volume)
+{
+	struct stat sb;
+
+	if (stat(volume, &sb))
+		return 0;
+	if (!S_ISBLK(sb.st_mode))
+		return 0;
+
+	return 1;
+}
+
+/**
+ * Checks an Volume value for its syntax and if it is already associated with
+ * another key. This is a callback function for function
+ * _keystore_change_association().
+ *
+ * @param[in] volume     the Volume value to check
+ * @param[in] remove     if true the volume is removed
+ * @param[out] normalized normalized value on return or NULL if no change
+ * @param[in] private    private data: struct volume_check
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+static int _keystore_volume_check(const char *volume, bool remove,
+				  char **normalized, void *private)
+{
+	struct volume_check *info = (struct volume_check *)private;
+	char *ch;
+	int rc;
+
+	*normalized = NULL;
+
+	if (strpbrk(volume, "*?") != NULL) {
+		warnx("Volume name can not contain '*' or '?'");
+		return -EINVAL;
+	}
+
+	info->volume = util_strdup(volume);
+	ch = strchr(info->volume, ':');
+	if (ch == NULL || strlen(ch + 1) == 0) {
+		warnx("Volume specification must contain a dm-crypt mapping "
+		      "name separated by a colon");
+		rc = -EINVAL;
+		goto out;
+	}
+
+	if (remove) {
+		rc = 0;
+		goto out;
+	}
+
+	/*
+	 * Strip off the ':dm-name' part, so that the volume filter only
+	 * matches the volume part.
+	 */
+	*ch = '\0';
+
+	if (!_keystore_is_block_device(info->volume)) {
+		warnx("Volume '%s' is not a block device or is not available",
+		      info->volume);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	rc = _keystore_process_filtered(info->keystore, NULL, info->volume,
+					NULL, _keystore_volume_check_process,
+					info);
+out:
+	free((void *)info->volume);
+	info->volume = NULL;
+	return rc;
+}
+
+/**
+ * Locks the repository against other processes.
+ *
+ * @param[in] keystore   the keystore
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+static int _keystore_lock_repository(struct keystore *keystore)
+{
+	char *lock_file_name;
+	struct stat sb;
+	int rc;
+
+	util_asprintf(&lock_file_name, "%s/%s", keystore->directory,
+		      LOCK_FILE_NAME);
+
+	if (stat(lock_file_name, &sb) == 0) {
+		keystore->lock_fd = open(lock_file_name, O_RDONLY);
+		if (keystore->lock_fd == -1) {
+			rc = -errno;
+			warnx("Failed to open lock file '%s': %s",
+			      lock_file_name,
+			      strerror(-rc));
+			goto out;
+		}
+	} else {
+		keystore->lock_fd = open(lock_file_name, O_CREAT | O_RDONLY,
+					 keystore->mode);
+		if (keystore->lock_fd == -1) {
+			rc = -errno;
+			warnx("Failed to create lock file '%s': %s",
+			      lock_file_name,
+			      strerror(-rc));
+			goto out;
+		}
+
+		if (fchown(keystore->lock_fd, geteuid(),
+			   keystore->owner) != 0) {
+			rc = -errno;
+			warnx("chown faild on file '%s': %s", lock_file_name,
+			      strerror(-rc));
+			return rc;
+		}
+	}
+
+	rc = flock(keystore->lock_fd, LOCK_EX);
+	if (rc == -1) {
+		rc = -errno;
+		warnx("Failed to obtain the file lock on '%s': %s",
+		      lock_file_name, strerror((-rc)));
+	}
+
+out:
+	free(lock_file_name);
+	return rc;
+}
+
+/**
+ * Unlocks the repository
+ *
+ * @param[in] keystore   the keystore
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+static int _keystore_unlock_repository(struct keystore *keystore)
+{
+	int rc;
+
+	if (keystore->lock_fd == -1)
+		return 0;
+
+	rc = flock(keystore->lock_fd, LOCK_UN);
+	if (rc == -1) {
+		rc = -errno;
+		warnx("Failed to release the file lock: %s", strerror((-rc)));
+	}
+
+	close(keystore->lock_fd);
+	keystore->lock_fd = -1;
+
+	return rc;
+}
+
+/**
+ * Allocates new keystore object
+ *
+ * @param[in]    directory     the directory where the keystore resides
+ * @param[in]    verbose       if true, verbose messages are printed
+ *
+ * @returns a new keystore object
+ */
+struct keystore *keystore_new(const char *directory, bool verbose)
+{
+	struct keystore *keystore;
+	struct stat sb;
+	int rc;
+
+	util_assert(directory != NULL, "Internal error: directory is NULL");
+
+	if (stat(directory, &sb) != 0) {
+		warnx("'%s' does not exist", directory);
+		return NULL;
+	}
+	if (!(sb.st_mode & S_IFDIR)) {
+		warnx("'%s' is not a directory", directory);
+		return NULL;
+	}
+	if (!util_path_is_readable(directory) ||
+	    !util_path_is_writable(directory)) {
+		warnx("Permission denied for '%s'", directory);
+		return NULL;
+	}
+	if (sb.st_mode & S_IWOTH) {
+		warnx("Directory '%s' is writable for others, this is not "
+		      "accepted", directory);
+		return NULL;
+	}
+
+	keystore = util_zalloc(sizeof(struct keystore));
+
+	keystore->owner = sb.st_gid;
+	keystore->mode = sb.st_mode & (S_IRUSR | S_IWUSR |
+				       S_IRGRP  | S_IWGRP |
+				       S_IROTH);
+	keystore->lock_fd = -1;
+	keystore->verbose = verbose;
+	keystore->directory = util_strdup(directory);
+	if (keystore->directory[strlen(keystore->directory)-1] == '/')
+		keystore->directory[strlen(keystore->directory)-1] = '\0';
+
+	rc = _keystore_lock_repository(keystore);
+	if (rc != 0) {
+		keystore_free(keystore);
+		return NULL;
+	}
+
+	pr_verbose(keystore, "Keystore in directory '%s' opened successfully",
+		   keystore->directory);
+	return keystore;
+}
+
+/**
+ * Sets a timestamp to be used as creation/update/reencipher time into
+ * the specified property
+ *
+ * @param[in] properties   the properties object
+ * @param[in] property     the name of the property to set
+ *
+ * @returns 0 on success, or a negative errno value on error
+ */
+static int _keystore_set_timestamp_property(struct properties *properties,
+					      const char *property)
+{
+	char *time_str;
+	struct tm *tm;
+	time_t t;
+	int rc;
+
+	t = time(NULL);
+	tm = localtime(&t);
+	util_assert(tm != NULL, "Internal error: tm is NULL");
+
+	time_str = util_zalloc(200);
+	rc = strftime(time_str, 200, "%F %T", tm);
+	util_assert(rc > 0, "Internal error: strftime failed");
+
+	rc = properties_set(properties, property, time_str);
+
+	free(time_str);
+	return rc;
+}
+
+/**
+ * Sets the default properties of a key, such as key-type, cipher-name, and
+ * IV-mode
+ *
+ * @param[in] key_props   the properties object
+ */
+static int _keystore_set_default_properties(struct properties *key_props)
+{
+	int rc;
+
+	rc = properties_set(key_props, PROP_NAME_KEY_TYPE, "CCA-AESDATA");
+	if (rc != 0)
+		return rc;
+
+	rc = properties_set(key_props, PROP_NAME_CIPHER, "paes");
+	if (rc != 0)
+		return rc;
+
+	rc = properties_set(key_props, PROP_NAME_IV_MODE, "plain64");
+	if (rc != 0)
+		return rc;
+
+	rc = _keystore_set_timestamp_property(key_props,
+					      PROP_NAME_CREATION_TIME);
+	if (rc != 0)
+		return rc;
+
+	return 0;
+}
+
+/**
+ * Creates an initial .info file for a key
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name        the name of the key
+ * @param[in] info_filename  the file name of the key info file
+ * @param[in] description textual description of the key (optional, can be NULL)
+ * @param[in] volumes     a comma separated list of volumes associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] apqns       a comma separated list of APQNs associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] sector_size the sector size to use with dm-crypt. It must be power
+ *                        of two and in range 512 - 4096 bytes. 0 means that
+ *                        the sector size is not specified and the system
+ *                        default is used.
+ */
+static int _keystore_create_info_file(struct keystore *keystore,
+				      const char *name,
+				      const char *info_filename,
+				      const char *description,
+				      const char *volumes, const char *apqns,
+				      size_t sector_size)
+{
+	struct volume_check vol_check = { .keystore = keystore, .name = name };
+	struct properties *key_props;
+	char temp[10];
+	int rc;
+
+	key_props = properties_new();
+	rc = _keystore_set_default_properties(key_props);
+	if (rc != 0)
+		goto out;
+
+	rc = properties_set(key_props, PROP_NAME_DESCRIPTION,
+			    description != NULL ? description : "");
+	if (rc != 0) {
+		warnx("Invalid characters in description");
+		goto out;
+	}
+
+	rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES,
+					  volumes != NULL ? volumes : "",
+					  "volume", _keystore_volume_check,
+					  &vol_check);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
+					  apqns != NULL ? apqns : "",
+					  "APQN", _keystore_apqn_check, NULL);
+	if (rc != 0)
+		goto out;
+
+	if (!_keystore_valid_sector_size(sector_size)) {
+		warnx("Invalid sector-size specified");
+		rc = -EINVAL;
+		goto out;
+	}
+	sprintf(temp, "%lu", sector_size);
+	rc = properties_set(key_props, PROP_NAME_SECTOR_SIZE,
+			    temp);
+	if (rc != 0) {
+		warnx("Invalid characters in sector-size");
+		goto out;
+	}
+
+	rc = properties_save(key_props, info_filename, 1);
+	if (rc != 0) {
+		pr_verbose(keystore,
+			   "Key info file '%s' could not be written: %s",
+			   info_filename, strerror(-rc));
+		goto out;
+	}
+
+	rc = _keystore_set_file_permission(keystore, info_filename);
+	if (rc != 0) {
+		remove(info_filename);
+		goto out;
+	}
+
+out:
+	properties_free(key_props);
+	return rc;
+}
+
+/**
+ * Extracts a card/domain pair from the specified APQns, or uses AUTOSELECT
+ * if no APQNs are specified.
+ */
+static int _keystore_get_card_domain(const char *apqns, unsigned int *card,
+				     unsigned int *domain)
+{
+	char **apqn_list;
+	char *normalized = NULL;
+	int rc = 0;
+
+	*card = AUTOSELECT;
+	*domain = AUTOSELECT;
+
+	if (apqns == NULL)
+		return 0;
+
+	apqn_list = str_list_split(apqns);
+	if (apqn_list[0] == NULL)
+		goto out;
+
+	rc = _keystore_apqn_check(apqn_list[0], 0, &normalized, NULL);
+	if (normalized != NULL)
+		free(normalized);
+	if (rc != 0)
+		goto out;
+
+	if (sscanf(apqn_list[0], "%x.%x", card, domain) != 2) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+out:
+	str_list_free_string_array(apqn_list);
+	return rc;
+}
+
+/**
+ * Generates a secure key by random and adds it to the key store
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name        the name of the key
+ * @param[in] description textual description of the key (optional, can be NULL)
+ * @param[in] volumes     a comma separated list of volumes associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] apqns       a comma separated list of APQNs associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] sector_size the sector size to use with dm-crypt. It must be power
+ *                        of two and in range 512 - 4096 bytes. 0 means that
+ *                        the sector size is not specified and the system
+ *                        default is used.
+ * @param[in] keybits     cryptographical size of the key in bits
+ * @param[in] xts         if true, an XTS key is generated
+ * @param[in] clear_key_file if not NULL the secure key is generated from the
+ *                        clear key contained in the file denoted here.
+ *                        if NULL, the secure key is generated by random.
+ * @param[in] pkey_fd     the file descriptor of /dev/pkey
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_generate_key(struct keystore *keystore, const char *name,
+			  const char *description, const char *volumes,
+			  const char *apqns, size_t sector_size,
+			  size_t keybits, bool xts, const char *clear_key_file,
+			  int pkey_fd)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	struct properties *key_props = NULL;
+	unsigned int card, domain;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out_free_key_filenames;
+
+	rc = _keystore_ensure_keyfiles_not_exist(&file_names, name);
+	if (rc != 0)
+		goto out_free_key_filenames;
+
+	rc = _keystore_get_card_domain(apqns, &card, &domain);
+	if (rc != 0)
+		goto out_free_key_filenames;
+
+	if (clear_key_file == NULL)
+		rc = generate_secure_key_random(pkey_fd,
+						file_names.skey_filename,
+						keybits, xts, card, domain,
+						keystore->verbose);
+	else
+		rc = generate_secure_key_clear(pkey_fd,
+					       file_names.skey_filename,
+					       keybits, xts, clear_key_file,
+					       card, domain,
+					       keystore->verbose);
+	if (rc != 0)
+		goto out_free_props;
+
+	rc = _keystore_set_file_permission(keystore, file_names.skey_filename);
+	if (rc != 0)
+		goto out_free_props;
+
+	rc = _keystore_create_info_file(keystore, name,
+					file_names.info_filename,
+					description, volumes, apqns,
+					sector_size);
+	if (rc != 0)
+		goto out_free_props;
+
+	pr_verbose(keystore,
+		   "Successfully generated a secure key in '%s' and key info "
+		   "in '%s'", file_names.skey_filename,
+		   file_names.info_filename);
+
+out_free_props:
+	if (key_props != NULL)
+		properties_free(key_props);
+	if (rc != 0 && rc != -EEXIST)
+		remove(file_names.skey_filename);
+out_free_key_filenames:
+	_keystore_free_key_filenames(&file_names);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to generate key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Imports a secure key from a file and adds it to the key store
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name        the name of the key
+ * @param[in] description textual description of the key (optional, can be NULL)
+ * @param[in] volumes     a comma separated list of volumes associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] apqns       a comma separated list of APQNs associated with this
+ *                        key (optional, can be NULL)
+ * @param[in] sector_size the sector size to use with dm-crypt. It must be power
+ *                        of two and in range 512 - 4096 bytes. 0 means that
+ *                        the sector size is not specified and the system
+ *                        default is used.
+ * @param[in] import_file The name of a secure key containing the kley to import
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_import_key(struct keystore *keystore, const char *name,
+			const char *description, const char *volumes,
+			const char *apqns, size_t sector_size,
+			const char *import_file)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	struct properties *key_props = NULL;
+	size_t secure_key_size;
+	u8 *secure_key;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+	util_assert(import_file != NULL, "Internal error: import_file is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out_free_key_filenames;
+
+	rc = _keystore_ensure_keyfiles_not_exist(&file_names, name);
+	if (rc != 0)
+		goto out_free_key_filenames;
+
+	secure_key = read_secure_key(import_file, &secure_key_size,
+				     keystore->verbose);
+	if (secure_key == NULL) {
+		rc = -ENOENT;
+		goto out_free_key_filenames;
+	}
+
+	rc = write_secure_key(file_names.skey_filename, secure_key,
+			      secure_key_size, keystore->verbose);
+	free(secure_key);
+	if (rc != 0)
+		goto out_free_props;
+
+	rc = _keystore_set_file_permission(keystore, file_names.skey_filename);
+	if (rc != 0)
+		goto out_free_props;
+
+	rc = _keystore_create_info_file(keystore, name,
+					file_names.info_filename,
+					description, volumes, apqns,
+					sector_size);
+	if (rc != 0)
+		goto out_free_props;
+
+	pr_verbose(keystore,
+		   "Successfully imported a secure key in '%s' and key info in '%s'",
+		   file_names.skey_filename, file_names.info_filename);
+
+out_free_props:
+	if (key_props != NULL)
+		properties_free(key_props);
+	if (rc != 0 && rc != -EEXIST)
+		remove(file_names.skey_filename);
+out_free_key_filenames:
+	_keystore_free_key_filenames(&file_names);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to import key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+
+/**
+ * Changes properties of a key in the keystore.
+ *
+ * @param[in] keystore    the key store
+ * @param[in] name        the name of the key
+ * @param[in] description textual description of the key. If NULL then the
+ *                        description is not changed.
+ * @param[in] volumes     a comma separated list of volumes associated with this
+ *                        key, or a volume prefixed with '+' or '-' to add or
+ *                        remove that volume respectively. If NULL then the
+ *                        volumes are not changed.
+ * @param[in] apqns       a comma separated list of APQNs associated with this
+ *                        key, or an APQN prefixed with '+' or '-' to add or
+ *                        remove that APQN respectively. IfNULL then the APQNs
+ *                        are not changed.
+ * @param[in] sector_size the sector size to use with dm-crypt. It must be power
+ *                        of two and in range 512 - 4096 bytes. 0 means that
+ *                        the sector size is not specified and the system
+ *                        default is used. Specify -1 if this property should
+ *                        not be changed.
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ *
+ */
+int keystore_change_key(struct keystore *keystore, const char *name,
+			const char *description, const char *volumes,
+			const char *apqns, long int sector_size)
+{
+	struct volume_check vol_check = { .keystore = keystore, .name = name };
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	struct properties *key_props = NULL;
+	char temp[10];
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+	if (rc != 0)
+		goto out;
+
+	key_props = properties_new();
+	rc = properties_load(key_props, file_names.info_filename, 1);
+	if (rc != 0) {
+		warnx("Key '%s' does not exist or is invalid", name);
+		goto out;
+	}
+
+	if (description != NULL) {
+		rc = properties_set(key_props, PROP_NAME_DESCRIPTION,
+				    description);
+		if (rc != 0) {
+			warnx("Invalid characters in description");
+			goto out;
+		}
+	}
+
+	if (volumes != NULL) {
+		rc = _keystore_change_association(key_props, PROP_NAME_VOLUMES,
+						  volumes, "volume",
+						  _keystore_volume_check,
+						  &vol_check);
+		if (rc != 0)
+			goto out;
+	}
+
+	if (apqns != NULL) {
+		rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
+						  apqns, "APQN",
+						  _keystore_apqn_check, NULL);
+		if (rc != 0)
+			goto out;
+	}
+
+	if (sector_size >= 0) {
+		if (!_keystore_valid_sector_size(sector_size)) {
+			warnx("Invalid sector-size specified");
+			rc = -EINVAL;
+			goto out;
+		}
+
+		sprintf(temp, "%lu", sector_size);
+		rc = properties_set(key_props, PROP_NAME_SECTOR_SIZE,
+				    temp);
+		if (rc != 0) {
+			warnx("Invalid characters in sector-size");
+			goto out;
+		}
+	}
+
+	rc = _keystore_set_timestamp_property(key_props, PROP_NAME_CHANGE_TIME);
+	if (rc != 0)
+		goto out;
+
+	rc = properties_save(key_props, file_names.info_filename, 1);
+	if (rc != 0) {
+		pr_verbose(keystore,
+			   "Key info file '%s' could not be written: %s",
+			   file_names.info_filename, strerror(-rc));
+		goto out;
+	}
+
+	rc = _keystore_set_file_permission(keystore, file_names.info_filename);
+	if (rc != 0)
+		goto out;
+
+	pr_verbose(keystore, "Successfully changed key '%s'", name);
+
+out:
+	_keystore_free_key_filenames(&file_names);
+	if (key_props != NULL)
+		properties_free(key_props);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to change key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Renames a key in the keystore
+ *
+ * @param[in] keystore the key store
+ * @param[in] name     the name of the key
+ * @param[in] newname  the new name of the key
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_rename_key(struct keystore *keystore, const char *name,
+			const char *newname)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	struct key_filenames new_names = { NULL, NULL, NULL };
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+	util_assert(newname != NULL, "Internal error: newname is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_get_key_filenames(keystore, newname, &new_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_not_exist(&new_names, newname);
+	if (rc != 0)
+		goto out;
+
+	if (rename(file_names.skey_filename, new_names.skey_filename) != 0) {
+		rc = -errno;
+		pr_verbose(keystore, "Failed to rename '%s': %s",
+			   file_names.skey_filename, strerror(-rc));
+		goto out;
+	}
+	if (rename(file_names.info_filename, new_names.info_filename) != 0) {
+		rc = -errno;
+		pr_verbose(keystore, "Failed to rename '%s': %s",
+			   file_names.info_filename, strerror(-rc));
+		rename(new_names.skey_filename, file_names.skey_filename);
+	}
+	if (_keystore_reencipher_key_exists(&file_names)) {
+		if (rename(file_names.renc_filename,
+			   new_names.renc_filename) != 0) {
+			rc = -errno;
+			pr_verbose(keystore, "Failed to rename '%s': %s",
+				   file_names.renc_filename, strerror(-rc));
+			rename(new_names.skey_filename,
+			       file_names.skey_filename);
+			rename(new_names.info_filename,
+			       file_names.info_filename);
+		}
+	}
+
+	pr_verbose(keystore, "Successfully renamed key '%s' to '%s'", name,
+		   newname);
+
+out:
+	_keystore_free_key_filenames(&file_names);
+	_keystore_free_key_filenames(&new_names);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to rename key '%s'to '%s': %s",
+			   name, newname, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Sets up a util_rec used for displaying key information
+ *
+ * @param[in] validation if true the record is used for validate, else it is
+ *                       used for display
+ *
+ * @returns a pointer to a set up  struct util_rec.
+ */
+static struct util_rec *_keystore_setup_record(bool validation)
+{
+	struct util_rec *rec;
+
+	rec = util_rec_new_long("-", ":", REC_KEY, 23, 54);
+	util_rec_def(rec, REC_KEY, UTIL_REC_ALIGN_LEFT, 54, REC_KEY);
+	if (validation)
+		util_rec_def(rec, REC_STATUS, UTIL_REC_ALIGN_LEFT, 54,
+			     REC_STATUS);
+	util_rec_def(rec, REC_DESCRIPTION, UTIL_REC_ALIGN_LEFT, 54,
+		     REC_DESCRIPTION);
+	util_rec_def(rec, REC_SEC_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20,
+		     REC_SEC_KEY_SIZE);
+	util_rec_def(rec, REC_CLR_KEY_SIZE, UTIL_REC_ALIGN_LEFT, 20,
+		     REC_CLR_KEY_SIZE);
+	util_rec_def(rec, REC_XTS, UTIL_REC_ALIGN_LEFT, 3, REC_XTS);
+	if (validation)
+		util_rec_def(rec, REC_MASTERKEY, UTIL_REC_ALIGN_LEFT, 54,
+			     REC_MASTERKEY);
+	util_rec_def(rec, REC_VOLUMES, UTIL_REC_ALIGN_LEFT, 54, REC_VOLUMES);
+	util_rec_def(rec, REC_APQNS, UTIL_REC_ALIGN_LEFT, 54, REC_APQNS);
+	util_rec_def(rec, REC_KEY_FILE, UTIL_REC_ALIGN_LEFT, 54, REC_KEY_FILE);
+	util_rec_def(rec, REC_SECTOR_SIZE, UTIL_REC_ALIGN_LEFT, 54,
+		     REC_SECTOR_SIZE);
+	util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54,
+		     REC_CREATION_TIME);
+	util_rec_def(rec, REC_CHANGE_TIME, UTIL_REC_ALIGN_LEFT, 54,
+			REC_CHANGE_TIME);
+	util_rec_def(rec, REC_REENC_TIME, UTIL_REC_ALIGN_LEFT, 54,
+			REC_REENC_TIME);
+
+	return rec;
+}
+
+static void _keystore_print_record(struct util_rec *rec,
+				   const char *name,
+				   struct properties *properties,
+				   bool validation, const char *skey_filename,
+				   size_t secure_key_size,
+				   size_t clear_key_bitsize, bool valid,
+				   bool is_old_mk, bool reenc_pending)
+{
+	char *volumes_argz = NULL;
+	size_t volumes_argz_len;
+	char *apqns_argz = NULL;
+	size_t sector_size = 0;
+	size_t apqns_argz_len;
+	char *description;
+	char *reencipher;
+	char *creation;
+	char *volumes;
+	char *change;
+	char *apqns;
+	char *temp;
+
+	description = properties_get(properties, PROP_NAME_DESCRIPTION);
+	volumes = properties_get(properties, PROP_NAME_VOLUMES);
+	if (volumes != NULL)
+		util_assert(argz_create_sep(volumes, ',',
+					    &volumes_argz,
+					    &volumes_argz_len) == 0,
+			    "Internal error: argz_create_sep failed");
+	apqns = properties_get(properties, PROP_NAME_APQNS);
+	if (apqns != NULL)
+		util_assert(argz_create_sep(apqns, ',',
+					    &apqns_argz,
+					    &apqns_argz_len) == 0,
+			    "Internal error: argz_create_sep failed");
+
+	temp = properties_get(properties, PROP_NAME_SECTOR_SIZE);
+	if (temp != NULL) {
+		util_assert(sscanf(temp, "%lu", &sector_size) == 1,
+			   "Internal error: sscanf failed");
+		free(temp);
+	}
+
+	creation = properties_get(properties, PROP_NAME_CREATION_TIME);
+	change = properties_get(properties, PROP_NAME_CHANGE_TIME);
+	reencipher = properties_get(properties, PROP_NAME_REENC_TIME);
+
+	util_rec_set(rec, REC_KEY, name);
+	if (validation)
+		util_rec_set(rec, REC_STATUS, valid ? "Valid" : "Invalid");
+	util_rec_set(rec, REC_DESCRIPTION,
+		     description != NULL ? description : "");
+	util_rec_set(rec, REC_SEC_KEY_SIZE, "%lu bytes", secure_key_size);
+	if (!validation || valid)
+		util_rec_set(rec, REC_CLR_KEY_SIZE, "%lu bits",
+			     clear_key_bitsize);
+	else
+		util_rec_set(rec, REC_CLR_KEY_SIZE, "(unknown)");
+	util_rec_set(rec, REC_XTS,
+		     IS_XTS(secure_key_size) ? "Yes" : "No");
+	if (validation) {
+		if (valid)
+			util_rec_set(rec, REC_MASTERKEY,
+				     is_old_mk ? "OLD CCA master key" :
+						     "CURRENT CCA master key");
+		else
+			util_rec_set(rec, REC_MASTERKEY, "(unknown)");
+	}
+	if (volumes_argz != NULL)
+		util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
+				  volumes_argz_len);
+	else
+		util_rec_set(rec, REC_VOLUMES, "(none)");
+	if (apqns_argz != NULL)
+		util_rec_set_argz(rec, REC_APQNS,
+				  apqns_argz, apqns_argz_len);
+	else
+		util_rec_set(rec, REC_APQNS, "(none)");
+	util_rec_set(rec, REC_KEY_FILE, skey_filename);
+	if (sector_size == 0)
+		util_rec_set(rec, REC_SECTOR_SIZE, "(system default)");
+	else
+		util_rec_set(rec, REC_SECTOR_SIZE, "%lu bytes",
+			     sector_size);
+	util_rec_set(rec, REC_CREATION_TIME, creation);
+	util_rec_set(rec, REC_CHANGE_TIME,
+		     change != NULL ? change : "(never)");
+	util_rec_set(rec, REC_REENC_TIME, "%s %s",
+		     reencipher != NULL ? reencipher : "(never)",
+		     reenc_pending ? "(re-enciphering pending)" : "");
+
+	util_rec_print(rec);
+
+	if (description != NULL)
+		free(description);
+	if (volumes != NULL)
+		free(volumes);
+	if (volumes_argz != NULL)
+		free(volumes_argz);
+	if (apqns != NULL)
+		free(apqns);
+	if (apqns_argz != NULL)
+		free(apqns_argz);
+	if (creation != NULL)
+		free(creation);
+	if (change != NULL)
+		free(change);
+	if (reencipher != NULL)
+		free(reencipher);
+}
+
+struct validate_info {
+	struct util_rec *rec;
+	int pkey_fd;
+	unsigned long int num_valid;
+	unsigned long int num_invalid;
+	unsigned long int num_warnings;
+};
+
+/**
+ * Displays the status of the associated APQNs.
+ *
+ * @param[in] properties  the properties of the key
+ * @param[in] name        the name of the key
+ *
+ * @returns 0 in case of success, 1 if at least one of the APQNs is not
+ *          available
+ */
+static int _keystore_display_apqn_status(struct properties *properties,
+					 const char *name)
+{
+	int i, rc, card, domain, warning = 0;
+	char **apqn_list;
+	char *apqns;
+
+	apqns = properties_get(properties, PROP_NAME_APQNS);
+	if (apqns == NULL)
+		return 0;
+	apqn_list = str_list_split(apqns);
+
+	for (i = 0; apqn_list[i] != NULL; i++) {
+
+		if (sscanf(apqn_list[i], "%x.%x", &card, &domain) != 2)
+			continue;
+
+		rc = _keystore_is_apqn_online(card, domain);
+		if (rc != 1) {
+			printf("WARNING: The APQN %02x.%04x associated with "
+			       "key '%s' is %s\n", card, domain, name,
+			       rc == -1 ? "not a CCA card" : "not online");
+			warning = 1;
+		}
+	}
+
+	if (warning)
+		printf("\n");
+
+	free(apqns);
+	str_list_free_string_array(apqn_list);
+	return warning;
+}
+/**
+ * Displays the status of the associated volumes.
+ *
+ * @param[in] properties  the properties of the key
+ * @param[in] name        the name of the key
+ *
+ * @returns 0 in case of success, 1 if at least one of the volumes is not
+ *          available
+ */
+static int _keystore_display_volume_status(struct properties *properties,
+					   const char *name)
+{
+	int i, warning = 0;
+	char **volume_list;
+	char *volumes;
+	char *ch;
+
+	volumes = properties_get(properties, PROP_NAME_VOLUMES);
+	if (volumes == NULL)
+		return 0;
+	volume_list = str_list_split(volumes);
+
+	for (i = 0; volume_list[i] != NULL; i++) {
+
+		ch = strchr(volume_list[i], ':');
+		if (ch != NULL)
+			*ch = '\0';
+
+		if (!_keystore_is_block_device(volume_list[i])) {
+			printf("WARNING: The volume '%s' associated with "
+			       "key '%s' is not available\n", volume_list[i],
+			       name);
+			warning = 1;
+		}
+	}
+
+	if (warning)
+		printf("\n");
+
+	free(volumes);
+	str_list_free_string_array(volume_list);
+	return warning;
+}
+
+/**
+ * Processing function for the key validate function. Prints validation
+ * information for the key to be validated.
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] name       the name of the key
+ * @param[in] properties the properties object of the key
+ * @param[in] file_names the file names used by this key
+ * @param[in] private    private data: struct validate_info
+ *
+ * @returns 0 if the validation is successful, a negative errno value otherwise
+ */
+static int _keystore_process_validate(struct keystore *keystore,
+				      const char *name,
+				      struct properties *properties,
+				      struct key_filenames *file_names,
+				      void *private)
+{
+	struct validate_info *info = (struct validate_info *)private;
+	size_t clear_key_bitsize;
+	size_t secure_key_size;
+	u8 *secure_key;
+	int is_old_mk;
+	int rc, valid;
+
+	rc = _keystore_ensure_keyfiles_exist(file_names, name);
+	if (rc != 0)
+		goto out;
+
+	secure_key = read_secure_key(file_names->skey_filename,
+				     &secure_key_size, keystore->verbose);
+	if (secure_key == NULL) {
+		rc = -ENOENT;
+		goto out;
+	}
+
+	rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size,
+				 &clear_key_bitsize, &is_old_mk,
+				 keystore->verbose);
+	if (rc != 0) {
+		valid = 0;
+		info->num_invalid++;
+		rc = 0;
+	} else {
+		info->num_valid++;
+		valid = 1;
+	}
+	free(secure_key);
+
+	_keystore_print_record(info->rec, name, properties, 1,
+			       file_names->skey_filename, secure_key_size,
+			       clear_key_bitsize, valid, is_old_mk,
+			       _keystore_reencipher_key_exists(file_names));
+
+	if (valid && is_old_mk) {
+		util_print_indented("WARNING: The secure key is currently "
+				    "enciphered with the OLD CCA master key "
+				    "and should be re-enciphered with the "
+				    "CURRENT CCA master key as soon as "
+				    "possible to avoid data loss\n", 0);
+		info->num_warnings++;
+	}
+	if (_keystore_display_apqn_status(properties, name) != 0)
+		info->num_warnings++;
+	if (_keystore_display_volume_status(properties, name) != 0)
+		info->num_warnings++;
+
+out:
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to validate key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Validates one or multiple keys in the keystore
+ *
+ * @param[in] keystore the key store
+ * @param[in] name_filter  the name filter to select the key (can be NULL)
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_validate_key(struct keystore *keystore, const char *name_filter,
+			  const char *apqn_filter, int pkey_fd)
+{
+	struct validate_info info;
+	struct util_rec *rec;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	rec = _keystore_setup_record(1);
+
+	info.pkey_fd = pkey_fd;
+	info.rec = rec;
+	info.num_valid = 0;
+	info.num_invalid = 0;
+	info.num_warnings = 0;
+
+	rc = _keystore_process_filtered(keystore, name_filter, NULL,
+					apqn_filter,
+					_keystore_process_validate, &info);
+
+	util_rec_free(rec);
+
+	if (rc != 0) {
+		pr_verbose(keystore, "Failed to validate keys: %s",
+			   strerror(-rc));
+	} else {
+		pr_verbose(keystore, "Successfully validated keys");
+		printf("%lu keys are valid, %lu keys are invalid, %lu "
+		       "warnings\n", info.num_valid, info.num_invalid,
+		       info.num_warnings);
+	}
+	return rc;
+}
+
+struct reencipher_params {
+	bool from_old;
+	bool to_new;
+	bool complete;
+	int inplace; /* -1 = autodetect, 0 = not in-place, 1 = in-place */
+};
+
+struct reencipher_info {
+	struct reencipher_params params;
+	int pkey_fd;
+	t_CSNBKTC dll_CSNBKTC;
+	unsigned long num_reenciphered;
+	unsigned long num_failed;
+	unsigned long num_skipped;
+};
+
+/**
+ * Perform the reencipherment of a key
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] name       the name of the key
+ * @param[in] dll_CSNBKTC the CCA key token change function
+ * @param[in] params     reenciphering parameters
+ * @param[in] secure_key a buffer containing the secure key
+ * @param[in] secure_key_size the size of the secure key
+ * @param[in] is_old_mk  if true the key is currently re-enciphered with the
+ *            OLD master key
+ * @returns 0 if the re-enciphering is successful, a negative errno value
+ *          otherwise, 1 if it was skipped
+ */
+static int _keystore_perform_reencipher(struct keystore *keystore,
+					const char *name,
+					t_CSNBKTC dll_CSNBKTC,
+					struct reencipher_params *params,
+					u8 *secure_key, size_t secure_key_size,
+					bool is_old_mk)
+{
+	int rc;
+
+	if (!params->from_old && !params->to_new) {
+		/* Autodetect reencipher mode */
+		if (is_old_mk) {
+			params->from_old = 1;
+			util_print_indented("The secure key is currently "
+					    "enciphered with the OLD CCA "
+					    "master key and is being "
+					    "re-enciphered with the CURRENT "
+					    "CCA master key\n", 0);
+		} else {
+			params->to_new = 1;
+			util_print_indented("The secure key is currently "
+					    "enciphered with the CURRENT CCA "
+					    "master key and is being "
+					    "re-enciphered with the NEW CCA "
+					    "master key\n", 0);
+		}
+	}
+
+	if (params->from_old) {
+		if (!is_old_mk) {
+			printf("The secure key '%s' is already enciphered "
+			       "with the CURRENT CCA master key\n", name);
+			return 1;
+		}
+
+		if (params->inplace == -1)
+			params->inplace = 1;
+
+		pr_verbose(keystore,
+			   "Secure key '%s' will be re-enciphered from OLD "
+			   "to the CURRENT CCA master key", name);
+
+		rc = key_token_change(dll_CSNBKTC,
+				      secure_key, secure_key_size,
+				      METHOD_OLD_TO_CURRENT,
+				      keystore->verbose);
+		if (rc != 0) {
+			warnx("Failed to re-encipher '%s' from OLD to "
+			      "CURRENT CCA master key", name);
+			return rc;
+		}
+	}
+	if (params->to_new) {
+		pr_verbose(keystore,
+			   "Secure key '%s' will be re-enciphered from "
+			   "CURRENT to the NEW CCA master key", name);
+
+		if (params->inplace == -1)
+			params->inplace = 0;
+
+		rc = key_token_change(dll_CSNBKTC,
+				      secure_key, secure_key_size,
+				      METHOD_CURRENT_TO_NEW,
+				      keystore->verbose);
+		if (rc != 0) {
+			warnx("Failed to re-encipher '%s' from CURRENT to "
+			      "NEW CCA master key", name);
+			return rc;
+		}
+	}
+
+	return 0;
+}
+
+/**
+ * Processing function for the key re-enciphering function.
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] name       the name of the key
+ * @param[in] properties the properties object of the key (not used here)
+ * @param[in] file_names the file names used by this key
+ * @param[in] private    private data: struct reencipher_info
+ *
+ * @returns 0 if the re-enciphering is successful, a negative errno value
+ *          otherwise
+ */
+static int _keystore_process_reencipher(struct keystore *keystore,
+					const char *name,
+					struct properties *properties,
+					struct key_filenames *file_names,
+					void *private)
+{
+	struct reencipher_info *info = (struct reencipher_info *)private;
+	struct reencipher_params params = info->params;
+	size_t clear_key_bitsize;
+	size_t secure_key_size;
+	u8 *secure_key = NULL;
+	char *out_file;
+	int is_old_mk;
+	char *temp;
+	int rc;
+
+	rc = _keystore_ensure_keyfiles_exist(file_names, name);
+	if (rc != 0)
+		goto out;
+
+	pr_verbose(keystore, "Complete reencipher: %d", params.complete);
+	pr_verbose(keystore, "In-place reencipher: %d", params.inplace);
+
+	if (params.complete) {
+		if (!_keystore_reencipher_key_exists(file_names)) {
+			warnx("Staged re-enciphering in not pending for key "
+			      "'%s', skipping",
+			      name);
+			info->num_skipped++;
+			rc = 0;
+			goto out;
+		}
+
+		printf("Completing re-enciphering for key '%s'\n", name);
+
+		params.inplace = 1;
+	}
+
+	secure_key = read_secure_key(params.complete ?
+						file_names->renc_filename :
+						file_names->skey_filename,
+				     &secure_key_size, keystore->verbose);
+	if (secure_key == NULL) {
+		rc = -ENOENT;
+		goto out;
+	}
+
+	rc = validate_secure_key(info->pkey_fd, secure_key, secure_key_size,
+				 &clear_key_bitsize, &is_old_mk,
+				 keystore->verbose);
+	if (rc != 0) {
+		if (params.complete) {
+			warnx("Key '%s' is not valid, re-enciphering is not "
+			      "completed", name);
+			warnx("Possibly the CCA master key not yet been set?");
+		} else {
+			warnx("Key '%s' is not valid, it is not re-enciphered",
+			      name);
+			info->num_skipped++;
+			rc = 0;
+		}
+		goto out;
+	}
+
+	if (!params.complete) {
+		printf("Re-enciphering key '%s'\n", name);
+
+		rc = _keystore_perform_reencipher(keystore, name,
+						  info->dll_CSNBKTC, &params,
+						  secure_key, secure_key_size,
+						  is_old_mk);
+		if (rc < 0)
+			goto out;
+		if (rc > 0) {
+			info->num_skipped++;
+			rc = 0;
+			goto out;
+		}
+	}
+
+	pr_verbose(keystore, "In-place reencipher: %d", params.inplace);
+
+	out_file = params.inplace == 1 ? file_names->skey_filename :
+					 file_names->renc_filename;
+	rc = write_secure_key(out_file, secure_key,
+			      secure_key_size, keystore->verbose);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_set_file_permission(keystore, out_file);
+	if (rc != 0)
+		goto out;
+
+	if (params.complete || params.inplace == 1) {
+		rc = _keystore_set_timestamp_property(properties,
+						      PROP_NAME_REENC_TIME);
+		if (rc != 0)
+			goto out;
+
+		rc = properties_save(properties, file_names->info_filename, 1);
+		if (rc != 0) {
+			pr_verbose(keystore,
+				   "Failed to write key info file '%s': %s",
+				   file_names->info_filename, strerror(-rc));
+			goto out;
+		}
+
+		rc = _keystore_set_file_permission(keystore,
+						   file_names->info_filename);
+		if (rc != 0)
+			goto out;
+	}
+
+	if (params.complete ||
+	    (params.inplace && _keystore_reencipher_key_exists(file_names))) {
+		if (remove(file_names->renc_filename) != 0) {
+			rc = -errno;
+			pr_verbose(keystore, "Failed to remove '%s': %s",
+				   file_names->renc_filename, strerror(-rc));
+			goto out;
+		}
+	}
+
+	if (params.inplace != 1) {
+		util_asprintf(&temp, "Staged re-enciphering has completed for "
+			      "key '%s'. Run 'zkey reencipher' with option "
+			      "'--complete' when the NEW CCA master key has "
+			      "been set (moved to the CURRENT master key "
+			      "register) to complete the re-enciphering "
+			      "process", name);
+		util_print_indented(temp, 0);
+		free(temp);
+	}
+
+	info->num_reenciphered++;
+
+out:
+	if (secure_key != NULL)
+		free(secure_key);
+
+	printf("\n");
+
+	if (rc != 0) {
+		info->num_failed++;
+		pr_verbose(keystore, "Failed to re-encipher key '%s': %s",
+			   name, strerror(-rc));
+		rc = 0;
+	}
+	return rc;
+}
+
+/**
+ * Reenciphers a key in the keystore
+ *
+ * @param[in] keystore the key store
+ * @param[in] name_filter  the name filter to select the key (can be NULL)
+ * @param[in] apqn_filter  the APQN filter to seletc the key (can be NULL)
+ * @param[in] from_old     If true the key is reenciphered from the OLD to the
+ *                         CURRENT CCA master key.
+ * @param[in] to_new       If true the key is reenciphered from the CURRENT to
+ *                         the OLD CCA master key.
+ * @param[in] inplace      if true, the key will be re-enciphere in-place
+ * @param[in] staged       if true, the key will be re-enciphere not in-place
+ * @param[in] complete     if true, a pending re-encipherment is completed
+ * Note: if both from Old and toNew are FALSE, then the reencipherement mode is
+ *       detected automatically. If both are TRUE then the key is reenciphered
+ *       from the OLD to the NEW CCA master key.
+ * Note: if both inplace and staged are FLASE, then the key is re-enciphered
+ *       inplace when for OLD-to-CURRENT, and is reenciphered staged for
+ *       CURRENT-to-NEW.
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
+			    const char *apqn_filter,
+			    bool from_old, bool to_new, bool inplace,
+			    bool staged, bool complete, int pkey_fd,
+			    t_CSNBKTC dll_CSNBKTC)
+{
+	struct reencipher_info info;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	info.params.from_old = from_old;
+	info.params.to_new = to_new;
+	info.params.inplace = -1;
+	if (inplace)
+		info.params.inplace = 1;
+	if  (staged)
+		info.params.inplace = 0;
+	info.params.complete = complete;
+	info.pkey_fd = pkey_fd;
+	info.dll_CSNBKTC = dll_CSNBKTC;
+	info.num_failed = 0;
+	info.num_reenciphered = 0;
+	info.num_skipped = 0;
+
+	rc = _keystore_process_filtered(keystore, name_filter, NULL,
+					apqn_filter,
+					_keystore_process_reencipher, &info);
+
+	if (rc != 0) {
+		pr_verbose(keystore, "Failed to re-encipher keys: %s",
+			   strerror(-rc));
+	} else {
+		pr_verbose(keystore, "Successfully re-enciphered keys");
+		printf("%lu keys re-enciphered, %lu keys skipped, %lu keys "
+		       "failed to re-encipher\n",
+		       info.num_reenciphered, info.num_skipped,
+		       info.num_failed);
+		if (info.num_failed > 0)
+			rc = -EIO;
+	}
+	return rc;
+}
+
+/**
+ * Copies (duplicates) a key in the keystore. Any existing volume associations
+ * are removed from the copy, because a volume can only be associated to one
+ * key. However, you can set new volume associations using the volumes
+ * parameter.
+ *
+ * @param[in] keystore the key store
+ * @param[in] name     the name of the key
+ * @param[in] newname  the new name of the key
+ * @param[in] volumes  a comma separated list of volumes associated with this
+ *                     key (optional, can be NULL)
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_copy_key(struct keystore *keystore, const char *name,
+		      const char *newname, const char *volumes)
+{
+	struct volume_check vol_check = { .keystore = keystore,
+					  .name = newname };
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	struct key_filenames new_names = { NULL, NULL, NULL };
+	struct properties *key_prop = NULL;
+	size_t secure_key_size;
+	u8 *secure_key;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+	util_assert(newname != NULL, "Internal error: newname is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_get_key_filenames(keystore, newname, &new_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_not_exist(&new_names, newname);
+	if (rc != 0)
+		goto out;
+
+	secure_key = read_secure_key(file_names.skey_filename,
+				     &secure_key_size, keystore->verbose);
+	if (secure_key == NULL) {
+		rc = -ENOENT;
+		goto out;
+	}
+
+	rc = write_secure_key(new_names.skey_filename, secure_key,
+			      secure_key_size, keystore->verbose);
+	free(secure_key);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_set_file_permission(keystore, new_names.skey_filename);
+	if (rc != 0)
+		goto out;
+
+	key_prop = properties_new();
+	rc = properties_load(key_prop, file_names.info_filename, 1);
+	if (rc != 0) {
+		warnx("Key '%s' does not exist or is invalid", name);
+		remove(file_names.skey_filename);
+		goto out;
+	}
+
+	/*
+	 * Remove any volume association, since a volume can only be associated
+	 * with one key
+	 */
+	rc = properties_set(key_prop, PROP_NAME_VOLUMES, "");
+	if (rc != 0)
+		goto out;
+
+	if (volumes != NULL) {
+		rc = _keystore_change_association(key_prop, PROP_NAME_VOLUMES,
+						  volumes,
+						  "volume",
+						  _keystore_volume_check,
+						  &vol_check);
+		if (rc != 0)
+			goto out;
+	}
+
+	rc = properties_remove(key_prop, PROP_NAME_CHANGE_TIME);
+	if (rc != 0 && rc != -ENOENT)
+		goto out;
+
+	rc = properties_remove(key_prop, PROP_NAME_REENC_TIME);
+	if (rc != 0 && rc != -ENOENT)
+		goto out;
+
+	rc = _keystore_set_timestamp_property(key_prop,
+					      PROP_NAME_CREATION_TIME);
+	if (rc != 0)
+		goto out;
+
+	rc = properties_save(key_prop, new_names.info_filename, 1);
+	if (rc != 0) {
+		pr_verbose(keystore,
+			   "Key info file '%s' could not be written: %s",
+			   new_names.info_filename, strerror(-rc));
+		remove(new_names.skey_filename);
+		goto out;
+	}
+
+	rc = _keystore_set_file_permission(keystore, new_names.info_filename);
+	if (rc != 0)
+		goto out;
+
+	pr_verbose(keystore, "Successfully copied key '%s' to '%s'", name,
+		   newname);
+
+out:
+	if (rc != 0) {
+		remove(new_names.skey_filename);
+		remove(new_names.info_filename);
+	}
+
+	_keystore_free_key_filenames(&file_names);
+	_keystore_free_key_filenames(&new_names);
+	if (key_prop != NULL)
+		properties_free(key_prop);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to copy key '%s'to '%s': %s",
+			   name, newname, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Exports a key from the keystore to a file
+ *
+ * @param[in] keystore the key store
+ * @param[in] name     the name of the key
+ * @param[in] export_file the name of the file to export the key to
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_export_key(struct keystore *keystore, const char *name,
+			const char *export_file)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	size_t secure_key_size;
+	u8 *secure_key;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+	util_assert(export_file != NULL, "Internal error: export_file is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+	if (rc != 0)
+		goto out;
+
+	secure_key = read_secure_key(file_names.skey_filename,
+				     &secure_key_size, keystore->verbose);
+	if (secure_key == NULL) {
+		rc = -ENOENT;
+		goto out;
+	}
+
+	rc = write_secure_key(export_file, secure_key,
+			      secure_key_size, keystore->verbose);
+	free(secure_key);
+
+	pr_verbose(keystore, "Successfully exported key '%s' to '%s'", name,
+		   export_file);
+
+out:
+	_keystore_free_key_filenames(&file_names);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to export key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Prompts the user to confirm deletion of a key
+ *
+ * @param[in] keystore the key store
+ * @param[in] name     the name of the key
+ * @param[in] file_names the file names of the key
+ *
+ * @returnd 0 if the user confirmed the deletion, a negative errno value
+ *          otherwise
+ */
+static int _keystore_propmp_for_remove(struct keystore *keystore,
+				       const char *name,
+				       struct key_filenames *file_names)
+{
+	struct properties *key_prop;
+	char *volumes = NULL;
+	char **volume_list = NULL;
+	char str[20];
+	int rc, i;
+
+	key_prop = properties_new();
+	rc = properties_load(key_prop, file_names->info_filename, 1);
+	if (rc != 0) {
+		warnx("Key '%s' does not exist or is invalid", name);
+		goto out;
+	}
+
+	volumes = properties_get(key_prop, PROP_NAME_VOLUMES);
+	if (volumes != NULL && strlen(volumes) > 0) {
+		volume_list = str_list_split(volumes);
+
+		warnx("When you remove key '%s' the following volumes will "
+		      "no longer be usable:", name);
+		for (i = 0; volume_list[i] != NULL; i++)
+			fprintf(stderr, "%s\n", volume_list[i]);
+	}
+
+	printf("%s: Remove key '%s'? ", program_invocation_short_name, name);
+	if (fgets(str, sizeof(str), stdin) == NULL) {
+		rc = -EIO;
+		goto out;
+	}
+	if (str[strlen(str) - 1] == '\n')
+		str[strlen(str) - 1] = '\0';
+	pr_verbose(keystore, "Prompt reply: '%s'", str);
+	if (strcasecmp(str, "y") != 0 && strcasecmp(str, "yes") != 0) {
+		rc = -ECANCELED;
+		goto out;
+	}
+
+out:
+	properties_free(key_prop);
+	if (volume_list != NULL)
+		str_list_free_string_array(volume_list);
+
+	return rc;
+}
+
+/**
+ * Removes a key from the keystore
+ *
+ * @param[in] keystore the key store
+ * @param[in] name     the name of the key
+ * @param[in] quiet    if true no confirmation prompt is shown
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_remove_key(struct keystore *keystore, const char *name,
+			bool quiet)
+{
+	struct key_filenames file_names = { NULL, NULL, NULL };
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+	util_assert(name != NULL, "Internal error: name is NULL");
+
+	rc = _keystore_get_key_filenames(keystore, name, &file_names);
+	if (rc != 0)
+		goto out;
+
+	rc = _keystore_ensure_keyfiles_exist(&file_names, name);
+	if (rc != 0)
+		goto out;
+
+	if (!quiet) {
+		if (_keystore_propmp_for_remove(keystore, name,
+						&file_names) != 0)
+			goto out;
+	}
+
+	if (remove(file_names.skey_filename) != 0) {
+		rc = -errno;
+		pr_verbose(keystore, "Failed to remove '%s': %s",
+			   file_names.skey_filename, strerror(-rc));
+		goto out;
+	}
+	if (remove(file_names.info_filename) != 0) {
+		rc = -errno;
+		pr_verbose(keystore, "Failed to remove '%s': %s",
+			   file_names.info_filename, strerror(-rc));
+	}
+	if (_keystore_reencipher_key_exists(&file_names)) {
+		if (remove(file_names.renc_filename) != 0) {
+			rc = -errno;
+			pr_verbose(keystore, "Failed to remove '%s': %s",
+				   file_names.renc_filename, strerror(-rc));
+		}
+	}
+	pr_verbose(keystore, "Successfully removed key '%s'", name);
+
+out:
+	_keystore_free_key_filenames(&file_names);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to remove key '%s': %s",
+			   name, strerror(-rc));
+	return rc;
+}
+
+/**
+ * Processing function for the key display function.
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] name       the name of the key
+ * @param[in] properties the properties object of the key
+ * @param[in] file_names the file names used by this key
+ * @param[in] private    private data: struct reencipher_info
+ *
+ * @returns 0 if the display is successful, a negative errno value otherwise
+ */
+static int _keystore_display_key(struct keystore *keystore,
+				 const char *name,
+				 struct properties *properties,
+				 struct key_filenames *file_names,
+				 void *private)
+{
+	struct util_rec *rec = (struct util_rec *)private;
+	struct secaeskeytoken *secure_key;
+	size_t secure_key_size;
+	int rc = 0;
+
+	secure_key = (struct secaeskeytoken *)
+		     read_secure_key(file_names->skey_filename,
+				     &secure_key_size, keystore->verbose);
+	if (secure_key == NULL)
+		return -EIO;
+
+	if (secure_key_size < SECURE_KEY_SIZE) {
+		pr_verbose(keystore,
+			   "Size of secure key is too small: %lu expected %lu",
+			   secure_key_size, SECURE_KEY_SIZE);
+		rc = -EIO;
+		goto out;
+	}
+
+	_keystore_print_record(rec, name, properties, 0,
+			       file_names->skey_filename, secure_key_size,
+			       IS_XTS(secure_key_size) ? secure_key->bitsize * 2
+						       : secure_key->bitsize,
+			       0, 0,
+			       _keystore_reencipher_key_exists(file_names));
+
+out:
+	free(secure_key);
+	return rc;
+}
+
+/**
+ * Lists keys in the keystore that matches the filters
+ *
+ * @param[in] keystore the key store
+ * @param[in] name_filter    the name filter. Can contain wild cards.
+ *                           NULL means no name filter.
+ * @param[in] volume_filter  the volume filter. Can contain wild cards, and
+ *                           mutliple volume filters separated by commas.
+ *                           The ':dm-name' part of the volume is optional
+ *                           for the volume filter. If not specified, the filter
+ *                           checks the volume part only.
+ *                           NULL means no volume filter.
+ * @param[in] apqn_filter    the APQN filter. Can contain wild cards, and
+ *                           mutliple APQN filters separated by commas.
+ *                           NULL means no APQN filter.
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_list_keys(struct keystore *keystore, const char *name_filter,
+		       const char *volume_filter, const char *apqn_filter)
+{
+	struct util_rec *rec;
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	rec = _keystore_setup_record(0);
+
+	rc = _keystore_process_filtered(keystore, name_filter, volume_filter,
+					apqn_filter, _keystore_display_key,
+					rec);
+	util_rec_free(rec);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Failed to list keys: %s",
+			   strerror(-rc));
+	else
+		pr_verbose(keystore, "Successfully listed keys");
+	return rc;
+}
+
+/**
+ * Executes a command via system().
+ *
+ * @param[in] cmd        the command to execute
+ * @param[in] msg_cmd    the short command name (for messages)
+ *
+ * @returns the exit code of the command execution, or -1 in case of an error
+ */
+static int _keystore_execute_cmd(const char *cmd,
+				 const char *msg_cmd)
+{
+	int rc;
+
+	rc = setenv("PATH", "/bin:/usr/bin:/usr/sbin", 1);
+	if (rc < 0)
+		return rc;
+
+	rc = system(cmd);
+	if (WIFEXITED(rc)) {
+		rc = WEXITSTATUS(rc);
+		if (rc != 0)
+			printf("%s exit code: %d\n", msg_cmd, rc);
+	} else {
+		rc = -EIO;
+		warnx("%s terminated abnormally", msg_cmd);
+	}
+
+	return rc;
+}
+
+
+struct crypt_info {
+	bool execute;
+	char **volume_filter;
+	int (*process_func)(struct keystore *keystore,
+			    const char *volume,
+			    const char *dmname,
+			    const char *cipher_spec,
+			    const char *key_file_name,
+			    size_t key_file_size,
+			    size_t sector_size,
+			    struct crypt_info *info);
+};
+
+/**
+ * Processing function for the cryptsetup function. Builds a cryptsetup command
+ * line and optionally executes it.
+ *
+ * @param[in] keystore   the keystore (not used here)
+ * @param[in] volume     the volume to mount
+ * @param[in] dmname     the debice mapper name
+ * @param[in] cipher_spec the cipher specification
+ * @param[in] key_file_name the key file name
+ * @param[in] key_file_size the size of the key file in bytes
+ * @param sector_size    the sector size in bytes or 0 if not specified
+ * @param[in] info       processing info
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+static int _keystore_process_cryptsetup(struct keystore *keystore,
+					const char *volume,
+					const char *dmname,
+					const char *cipher_spec,
+					const char *key_file_name,
+					size_t key_file_size,
+					size_t sector_size,
+					struct crypt_info *info)
+{
+	char temp[100];
+	int rc = 0;
+	char *cmd;
+
+	sprintf(temp, "--sector-size %lu ", sector_size);
+	util_asprintf(&cmd,
+		      "cryptsetup plainOpen %s--key-file '%s' --key-size %lu "
+		      "--cipher %s %s%s %s",
+		      keystore->verbose ? "-v " : "", key_file_name,
+		      key_file_size * 8, cipher_spec,
+		      sector_size > 0 ? temp : "", volume, dmname);
+
+	if (info->execute) {
+		printf("Executing: %s\n", cmd);
+		rc = _keystore_execute_cmd(cmd, "cryptsetup");
+	} else {
+		printf("%s\n", cmd);
+	}
+
+	free(cmd);
+	return rc;
+}
+
+/**
+ * Processing function for the crypttab function. Builds a crypttab entry
+ * and prints it.
+ *
+ * @param[in] keystore   the keystore (not used here)
+ * @param[in] volume     the volume to mount
+ * @param[in] dmname     the debice mapper name
+ * @param[in] cipher_spec the cipher specification
+ * @param[in] key_file_name the key file name
+ * @param[in] key_file_size the size of the key file in bytes
+ * @param sector_size    the sector size in bytes or 0 if not specified
+ * @param[in] info       processing info (not used here)
+ *
+ * @returns 0 if successful, a negative errno value otherwise
+ */
+
+static int _keystore_process_crypttab(struct keystore *UNUSED(keystore),
+				      const char *volume,
+				      const char *dmname,
+				      const char *cipher_spec,
+				      const char *key_file_name,
+				      size_t key_file_size,
+				      size_t sector_size,
+				      struct crypt_info *UNUSED(info))
+{
+	char temp[1000];
+
+	if (sector_size > 0) {
+		sprintf(temp,
+			"WARNING: volume '%s' is using a sector size of %lu. "
+			"At the time this utility was developed, systemd's "
+			"support of crypttab did not support to specify a "
+			"sector size with plain dm-crypt devices. The generated "
+			"crypttab entry may or may not work, and may need "
+			"manual adoptions.", volume, sector_size);
+		util_print_indented(temp, 0);
+	}
+
+	sprintf(temp, ",sector-size=%lu", sector_size);
+	printf("%s\t%s\t%s\tplain,cipher=%s,size=%lu,hash=plain%s\n",
+	       dmname, volume, key_file_name, cipher_spec, key_file_size * 8,
+	       sector_size > 0 ? temp : "");
+
+	return 0;
+}
+
+/**
+ * Builds a cipher specification for cryptsetup/crypttab
+ *
+ * @param properties    the key properties
+ * @param is_xts	if true, the key is an XTS key
+ *
+ * @returns the cipher spec string (must be freed by the caller)
+ */
+static char *_keystore_build_cipher_spec(struct properties *properties,
+					 bool is_xts)
+{
+	char *cipher_spec = NULL;
+	char *cipher = NULL;
+	char *ivmode = NULL;
+
+	cipher = properties_get(properties, PROP_NAME_CIPHER);
+	if (cipher == NULL)
+		goto out;
+
+	ivmode = properties_get(properties, PROP_NAME_IV_MODE);
+	if (ivmode == NULL)
+		goto out;
+
+	util_asprintf(&cipher_spec, "%s-%s-%s", cipher, is_xts ? "xts" : "cbc",
+		     ivmode);
+
+out:
+	if (cipher != NULL)
+		free(cipher);
+	if (ivmode != NULL)
+		free(ivmode);
+
+	return cipher_spec;
+}
+
+/**
+ * Returns the size of the secure key file
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] skey_filename the file name of the secure key
+ *
+ * @returns the size of the secure key, or -1 in case of an error
+ */
+static size_t _keystore_get_key_file_size(struct keystore *keystore,
+					  const char *skey_filename)
+{
+	size_t secure_key_size;
+	struct stat sb;
+
+	if (stat(skey_filename, &sb)) {
+		pr_verbose(keystore, "Key file '%s': %s",
+			   skey_filename, strerror(errno));
+		return -1;
+	}
+
+	secure_key_size = sb.st_size;
+	if (secure_key_size < SECURE_KEY_SIZE) {
+		pr_verbose(keystore,
+			   "Size of secure key is too small: %lu expected %lu",
+			   secure_key_size, SECURE_KEY_SIZE);
+		return -1;
+	}
+
+	return secure_key_size;
+}
+
+/**
+ * Processing function for the cryptsetup and crypttab functions.
+ * Extracts the required information and calls the secondary processing function
+ * contained in struct crypt_info.
+ *
+ * @param[in] keystore   the keystore
+ * @param[in] name       the name of the key
+ * @param[in] properties the properties object of the key
+ * @param[in] file_names the file names used by this key
+ * @param[in] private    private data: struct crypt_info
+ *
+ * @returns 0 if the validation is successful, a negative errno value otherwise
+ */
+static int _keystore_process_crypt(struct keystore *keystore,
+				   const char *name,
+				   struct properties *properties,
+				   struct key_filenames *file_names,
+				   void *private)
+{
+	struct crypt_info *info = (struct crypt_info *)private;
+	char **volume_list = NULL;
+	char *cipher_spec = NULL;
+	size_t secure_key_size;
+	size_t sector_size = 0;
+	char *volumes = NULL;
+	char *dmname;
+	char *temp;
+	int rc = 0;
+	char *vol;
+	char *ch;
+	int i;
+
+	secure_key_size = _keystore_get_key_file_size(keystore,
+						file_names->skey_filename);
+	if (secure_key_size < SECURE_KEY_SIZE) {
+		pr_verbose(keystore,
+			   "Size of secure key is too small: %lu expected %lu",
+			   secure_key_size, SECURE_KEY_SIZE);
+		rc = -EIO;
+		goto out;
+	}
+
+	cipher_spec = _keystore_build_cipher_spec(properties,
+						  IS_XTS(secure_key_size));
+	if (cipher_spec == NULL) {
+		rc = -EINVAL;
+		goto out;
+	}
+
+	volumes = properties_get(properties, PROP_NAME_VOLUMES);
+	if (volumes == NULL)
+		return -EINVAL;
+	volume_list = str_list_split(volumes);
+
+	temp = properties_get(properties, PROP_NAME_SECTOR_SIZE);
+	if (temp != NULL) {
+		util_assert(sscanf(temp, "%lu", &sector_size) == 1,
+			   "Internal error: sscanf failed");
+		free(temp);
+	}
+
+	for (i = 0; volume_list[i] != NULL && rc == 0; i++) {
+		vol = volume_list[i];
+		if (_keystore_match_filter(vol, info->volume_filter,
+					   NULL) != 0) {
+			ch = strchr(vol, ':');
+			if (ch == NULL) {
+				warnx("Volume does not contain a dm-name part."
+				      " Key: '%s'", name);
+				rc = -EINVAL;
+				break;
+			}
+			*ch = '\0';
+			dmname = ch + 1;
+
+			rc = info->process_func(keystore, vol, dmname,
+					cipher_spec, file_names->skey_filename,
+					secure_key_size, sector_size, info);
+			if (rc != 0)
+				break;
+		}
+	}
+
+out:
+	if (volumes != NULL)
+		free(volumes);
+	if (volume_list != NULL)
+		str_list_free_string_array(volume_list);
+	if (cipher_spec != NULL)
+		free(cipher_spec);
+	return rc;
+}
+
+/**
+ * Generates cryptsetup commands for one or multiple volumes.
+ *
+ * @param[in] keystore       the key store
+ * @param[in] volume_filter  the volume filter. Can contain wild cards, and
+ *                           mutliple volume filters separated by commas.
+ *                           The ':dm-name' part of the volume is optional
+ *                           for the volume filter. If not specified, the filter
+ *                           checks the volume part only.
+ * @param[in] execute        If TRUE the cryptsetup command is executed,
+ *                           otherwise it is printed to stdout
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
+			bool execute)
+{
+	struct crypt_info info = { 0 };
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	if (volume_filter == NULL)
+		volume_filter = "*";
+	info.execute = execute;
+	info.volume_filter = str_list_split(volume_filter);
+	info.process_func = _keystore_process_cryptsetup;
+
+	rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL,
+					_keystore_process_crypt, &info);
+
+	str_list_free_string_array(info.volume_filter);
+
+	if (rc < 0)
+		pr_verbose(keystore, "Cryptsetup failed with: %s",
+			   strerror(-rc));
+	else if (rc > 0)
+		pr_verbose(keystore, "Cryptsetup failed with: %d", rc);
+	else
+		pr_verbose(keystore,
+			   "Successfully generated cryptsetup commands");
+
+	return rc;
+}
+
+/**
+ * Generates crypttab entries for one or multiple volumes.
+ *
+ * @param[in] keystore       the key store
+ * @param[in] volume_filter  the volume filter. Can contain wild cards, and
+ *                           mutliple volume filters separated by commas.
+ *                           The ':dm-name' part of the volume is optional
+ *                           for the volume filter. If not specified, the filter
+ *                           checks the volume part only.
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int keystore_crypttab(struct keystore *keystore, const char *volume_filter)
+{
+	struct crypt_info info = { 0 };
+	int rc;
+
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	if (volume_filter == NULL)
+		volume_filter = "*";
+	info.volume_filter = str_list_split(volume_filter);
+	info.process_func = _keystore_process_crypttab;
+
+	rc = _keystore_process_filtered(keystore, NULL, volume_filter, NULL,
+					_keystore_process_crypt, &info);
+
+	str_list_free_string_array(info.volume_filter);
+
+	if (rc != 0)
+		pr_verbose(keystore, "Cryptsetup failed with: %s",
+			   strerror(-rc));
+	else
+		pr_verbose(keystore, "Successfully generated crypttab entries");
+
+	return rc;
+}
+
+/**
+ * Frees a keystore object
+ *
+ * @param[in] keystore the key store
+ */
+void keystore_free(struct keystore *keystore)
+{
+	util_assert(keystore != NULL, "Internal error: keystore is NULL");
+
+	_keystore_unlock_repository(keystore);
+	free(keystore->directory);
+	free(keystore);
+}
--- /dev/null
+++ b/zkey/keystore.h
@@ -0,0 +1,77 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Keystore handling functions
+ *
+ * Copyright IBM Corp. 2018
+ *
+ * s390-tools is free software; you can redistribute it and/or modify
+ * it under the terms of the MIT license. See LICENSE for details.
+ */
+
+#ifndef KEYSTORE_H
+#define KEYSTORE_H
+
+#include <stdbool.h>
+
+#include "pkey.h"
+
+struct keystore {
+	bool verbose;
+	char *directory;
+	int lock_fd;
+	mode_t mode;
+	gid_t owner;
+};
+
+struct keystore *keystore_new(const char *directory, bool verbose);
+
+int keystore_generate_key(struct keystore *keystore, const char *name,
+			  const char *description, const char *volumes,
+			  const char *apqns, size_t sector_size,
+			  size_t keybits, bool xts, const char *clear_key_file,
+			  int pkey_fd);
+
+int keystore_import_key(struct keystore *keystore, const char *name,
+			const char *description, const char *volumes,
+			const char *apqns, size_t sector_size,
+			const char *import_file);
+
+int keystore_change_key(struct keystore *keystore, const char *name,
+			const char *description, const char *volumes,
+			const char *apqns, long int sector_size);
+
+int keystore_rename_key(struct keystore *keystore, const char *name,
+			const char *newname);
+
+int keystore_validate_key(struct keystore *keystore, const char *name_filter,
+			  const char *apqn_filter, int pkey_fd);
+
+int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
+			    const char *apqn_filter,
+			    bool from_old, bool to_new, bool inplace,
+			    bool staged, bool complete, int pkey_fd,
+			    t_CSNBKTC dll_CSNBKTC);
+
+int keystore_copy_key(struct keystore *keystore, const char *name,
+		      const char *newname, const char *volumes);
+
+int keystore_export_key(struct keystore *keystore, const char *name,
+			const char *export_file);
+
+int keystore_remove_key(struct keystore *keystore, const char *name,
+			bool quiet);
+
+int keystore_list_keys(struct keystore *keystore, const char *name_filter,
+		       const char *volume_filter, const char *apqn_filter);
+
+int keystore_cryptsetup(struct keystore *keystore, const char *volume_filter,
+			bool execute);
+
+int keystore_crypttab(struct keystore *keystore, const char *volume_filter);
+
+void keystore_free(struct keystore *keystore);
+
+
+
+#endif
