diff --git a/zkey/keystore.c b/zkey/keystore.c index d43045b4..d8ebe2cd 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -1588,11 +1588,10 @@ static int _keystore_set_default_properties(struct properties *key_props) } /** - * Creates an initial .info file for a key + * Creates the key properties 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) @@ -1607,17 +1606,18 @@ static int _keystore_set_default_properties(struct properties *key_props) * @param[in] volume_type the type of volume * @param[in] key_type the type of the key * @param[in] kms the name of the KMS plugin, or NULL if no KMS is bound + * @param[out] props the properties object is allocated and returned */ -static int _keystore_create_info_file(struct keystore *keystore, - const char *name, - const struct key_filenames *filenames, - const char *description, - const char *volumes, const char *apqns, - bool noapqncheck, - size_t sector_size, - const char *volume_type, - const char *key_type, - const char *kms) +static int _keystore_create_info_props(struct keystore *keystore, + const char *name, + const char *description, + const char *volumes, const char *apqns, + bool noapqncheck, + size_t sector_size, + const char *volume_type, + const char *key_type, + const char *kms, + struct properties **props) { struct volume_check vol_check = { .keystore = keystore, .name = name, .set = 0 }; @@ -1629,6 +1629,8 @@ static int _keystore_create_info_file(struct keystore *keystore, char temp[10]; int rc; + *props = NULL; + key_props = properties_new(); rc = _keystore_set_default_properties(key_props); if (rc != 0) @@ -1696,13 +1698,65 @@ static int _keystore_create_info_file(struct keystore *keystore, } } +out: + if (rc == 0) + *props = key_props; + else + properties_free(key_props); + + return rc; +} + + +/** + * 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] noapqncheck if true, the specified APQN(s) are not checked for + * existence and type. + * @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] volume_type the type of volume + * @param[in] key_type the type of the key + * @param[in] kms the name of the KMS plugin, or NULL if no KMS is bound + */ +static int _keystore_create_info_file(struct keystore *keystore, + const char *name, + const struct key_filenames *filenames, + const char *description, + const char *volumes, const char *apqns, + bool noapqncheck, + size_t sector_size, + const char *volume_type, + const char *key_type, + const char *kms) +{ + struct properties *key_props = NULL; + int rc; + + rc = _keystore_create_info_props(keystore, name, description, volumes, + apqns, noapqncheck, sector_size, + volume_type, key_type, kms, + &key_props); + if (rc != 0) + return rc; + rc = _keystore_ensure_vp_exists(keystore, filenames, key_props); if (rc != 0) { warnx("Failed to generate the key verification pattern: %s", strerror(-rc)); warnx("Make sure that kernel module 'paes_s390' is loaded and " "that the 'paes' cipher is available"); - return rc; + goto out; } rc = properties_save(key_props, filenames->info_filename, 1); @@ -1843,6 +1897,164 @@ out_free_key_filenames: return rc; } +/** + * Generates a secure key by using a KMS plugin 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] 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] volume_type the type of volume + * @param[in] key_type the type of the key (can be NULL) + * @param[in] kms_options an array of KMS options specified, or NULL if no + * KMS options have been specified + * @param[in] num_kms_options the number of options in above array + * + * @returns 0 for success or a negative errno in case of an error + */ +int keystore_generate_key_kms(struct keystore *keystore, const char *name, + const char *description, const char *volumes, + size_t sector_size, size_t keybits, bool xts, + const char *volume_type, const char *key_type, + struct kms_option *kms_options, + size_t num_kms_options) +{ + struct key_filenames file_names = { NULL, NULL, NULL }; + struct properties *key_props = NULL; + struct kms_info *kms_info; + char *apqns = NULL; + int rc, i; + + static const char * const key_types[] = { + KEY_TYPE_CCA_AESDATA, + KEY_TYPE_CCA_AESCIPHER, + KEY_TYPE_EP11_AES, + NULL + }; + + util_assert(keystore != NULL, "Internal error: keystore is NULL"); + util_assert(name != NULL, "Internal error: name is NULL"); + + kms_info = keystore->kms_info; + if (kms_info->plugin_lib == NULL) { + warnx("The repository is not bound to a KMS plugin"); + return -ENOENT; + } + + if (key_type == NULL) { + for (i = 0; kms_info->funcs->kms_supports_key_type != NULL && + key_types[i] != NULL; i++) { + if (kms_info->funcs->kms_supports_key_type( + kms_info->handle, key_types[i])) { + key_type = key_types[i]; + break; + } + } + if (key_type == NULL) + key_type = KEY_TYPE_CCA_AESDATA; + } + + if (!_keystore_valid_key_type(key_type)) { + warnx("Invalid key-type specified"); + return -EINVAL; + } + + 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 = get_kms_apqns_for_key_type(kms_info, key_type, true, &apqns, + keystore->verbose); + if (rc != 0) { + if (rc == -ENOTSUP) + warnx("Key-type not supported by the KMS plugin '%s'", + kms_info->plugin_name); + goto out_free_key_filenames; + } + + pr_verbose(keystore, "APQNs for keytype %s: '%s'", key_type, apqns); + + rc = _keystore_create_info_props(keystore, name, description, volumes, + apqns, false, sector_size, volume_type, + key_type, kms_info->plugin_name, + &key_props); + if (rc != 0) + goto out_free_key_filenames; + + rc = generate_kms_key(kms_info, name, key_type, key_props, xts, + keybits, file_names.skey_filename, + kms_options, num_kms_options, keystore->verbose); + if (rc != 0) { + warnx("KMS plugin '%s' failed to generate key '%s': %s", + kms_info->plugin_name, name, strerror(-rc)); + print_last_kms_error(kms_info); + goto out_free_props; + } + + rc = _keystore_set_file_permission(keystore, file_names.skey_filename); + if (rc != 0) + goto out_free_props; + + rc = _keystore_ensure_vp_exists(keystore, &file_names, key_props); + if (rc != 0) { + warnx("Failed to generate the key verification pattern: %s", + strerror(-rc)); + warnx("Make sure that kernel module 'paes_s390' is loaded and " + "that the 'paes' cipher is available"); + goto out_free_props; + } + + 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_del_info_file; + } + + rc = _keystore_set_file_permission(keystore, file_names.info_filename); + if (rc != 0) { + remove(file_names.info_filename); + goto out_del_info_file; + } + + pr_verbose(keystore, + "Successfully generated a secure key with KMS plugin '%s' " + "in '%s' and key info in '%s'", kms_info->plugin_name, + file_names.skey_filename, file_names.info_filename); + +out_del_info_file: + if (rc != 0) + remove(file_names.info_filename); +out_free_props: + if (key_props != NULL) + properties_free(key_props); + if (rc != 0) + remove(file_names.skey_filename); +out_free_key_filenames: + _keystore_free_key_filenames(&file_names); + if (apqns != NULL) + free(apqns); + + if (rc != 0) + pr_verbose(keystore, "Failed to generate key '%s' with KMS " + "plugin '%s': %s", name, kms_info->plugin_name, + strerror(-rc)); + return rc; + +} + /** * Imports a secure key from a file and adds it to the key store * diff --git a/zkey/keystore.h b/zkey/keystore.h index 5573120c..8c9ca7cf 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -56,6 +56,13 @@ int keystore_generate_key(struct keystore *keystore, const char *name, const char *clear_key_file, const char *volume_type, const char *key_type, int pkey_fd); +int keystore_generate_key_kms(struct keystore *keystore, const char *name, + const char *description, const char *volumes, + size_t sector_size, size_t keybits, bool xts, + const char *volume_type, const char *key_type, + struct kms_option *kms_options, + size_t num_kms_options); + int keystore_import_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, diff --git a/zkey/kms.c b/zkey/kms.c index d345175a..8f522125 100644 --- a/zkey/kms.c +++ b/zkey/kms.c @@ -7,6 +7,7 @@ * it under the terms of the MIT license. See LICENSE for details. */ +#include #include #include #include @@ -19,6 +20,7 @@ #include #include #include +#include #include #include "lib/util_base.h" @@ -45,6 +47,19 @@ #define KMS_CONFIG_PROP_EP11_APQNS "cca_apqns" #define KMS_CONFIG_LOCAL "local" +#define KMS_KEY_PROP_NAME "zkey-name" +#define KMS_KEY_PROP_CIPHER "cipher" +#define KMS_KEY_PROP_IV_MODE "iv-mode" +#define KMS_KEY_PROP_DESCRIPTION "description" +#define KMS_KEY_PROP_VOLUMES "volumes" +#define KMS_KEY_PROP_VOLUME_TYPE "volume-type" +#define KMS_KEY_PROP_SECTOR_SIZE "sector-size" +#define KMS_KEY_PROP_XTS_KEY "xts-key" +#define KMS_KEY_PROP_XTS_KEY1_ID "xts-key1-id" +#define KMS_KEY_PROP_XTS_KEY2_ID "xts-key2-id" +#define KMS_KEY_PROP_XTS_KEY1_LABEL "xts-key1-label" +#define KMS_KEY_PROP_XTS_KEY2_LABEL "xts-key2-label" + static const char * const key_types[] = { KEY_TYPE_CCA_AESDATA, KEY_TYPE_CCA_AESCIPHER, @@ -1897,3 +1912,400 @@ int reencipher_kms(struct kms_info *kms_info, bool from_old, bool to_new, out: return rc; } + +/** + * Performs a login with the KMS plugin, if one is configured + * + * @param[in] kms_info information of the currently bound plugin. + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 for success or a negative errno in case of an error. + */ +int perform_kms_login(struct kms_info *kms_info, bool verbose) +{ + int rc = 0; + + util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); + + if (kms_info->plugin_lib == NULL) { + rc = -ENOENT; + warnx("The repository is not bound to a KMS plugin"); + goto out; + } + + if (kms_info->funcs->kms_login == NULL) { + pr_verbose(verbose, "The KMS plugin does not support login"); + goto out; + } + + rc = kms_info->funcs->kms_login(kms_info->handle); + if (rc != 0) { + warnx("Failed to login into the KMS: %s", + strerror(-rc)); + print_last_kms_error(kms_info); + goto out; + } + +out: + return rc; +} + +/** + * Gets the subset of the APQNs of a KMS plugin for a specific key type + * + * @param[in] kms_info information of the currently bound plugin. + * @param[in] key_type the key type to get the APQNs for, or NULL to + * get all APQNs associated with the KMS + * @param[in] cross_check if true, the APQNs are cross checked + * @param[out] apqns On return the list of APQNs as comma separated + * string. Must be freed by the caller. + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 for success or a negative errno in case of an error. + * If the KMS plugin does not support the key type, then -ENOTSUP is returned + */ +int get_kms_apqns_for_key_type(struct kms_info *kms_info, const char *key_type, + bool cross_check, char **apqns, bool verbose) +{ + const struct fw_version *fw_version; + const char *prop_name; + int rc = 0, min_level; + enum card_type type; + + util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); + util_assert(apqns != NULL, "Internal error: apqns is NULL"); + + if (kms_info->plugin_lib == NULL) { + warnx("The repository is not bound to a KMS plugin"); + return -ENOENT; + } + + if (key_type != NULL) { + if (kms_info->funcs->kms_supports_key_type == NULL) + return -ENOTSUP; + + if (!kms_info->funcs->kms_supports_key_type(kms_info->handle, + key_type)) + return -ENOTSUP; + } + + type = get_card_type_for_keytype(key_type); + switch (type) { + case CARD_TYPE_CCA: + prop_name = KMS_CONFIG_PROP_CCA_APQNS; + break; + case CARD_TYPE_EP11: + prop_name = KMS_CONFIG_PROP_EP11_APQNS; + break; + default: + prop_name = KMS_CONFIG_PROP_APQNS; + break; + } + + *apqns = properties_get(kms_info->props, prop_name); + if (*apqns == NULL) + return -ENOTSUP; + + if (str_list_count(*apqns) == 0) { + rc = -ENOTSUP; + goto out; + } + + if (cross_check) { + min_level = get_min_card_level_for_keytype(key_type); + fw_version = get_min_fw_version_for_keytype(key_type); + + rc = cross_check_apqns(*apqns, NULL, min_level, fw_version, + type, true, verbose); + if (rc == -ENOTSUP) + rc = 0; + if (rc != 0) { + warnx("Your master key setup is improper"); + goto out; + } + } + +out: + if (rc != 0) { + free(*apqns); + *apqns = NULL; + } + + return rc; +} + +/** + * Returns a system specific version of the specified properties name. + * The properties name does not contain any special characters, except '-' + * and '_'. The returned string mst be freed by the caller. + * + * @param[in] prop_name the base property name + * + * @returns an allocated string + */ +static char *_get_system_specific_prop_name(const char *prop_name) +{ + struct utsname utsname; + char *ret; + int i; + + if (uname(&utsname)) { + warnx("uname failed: %s", strerror(errno)); + return NULL; + } + + for (i = 0; utsname.nodename[i] != '\0'; i++) + if (!isalnum(utsname.nodename[i])) + utsname.nodename[i] = '_'; + + util_asprintf(&ret, "%s-%s", prop_name, utsname.nodename); + return ret; +} + +#define ADD_KMS_PROPS(props, num, pname, pvalue) \ + do { \ + util_assert((num) * sizeof(struct kms_property) < \ + sizeof(props), " Internal error: kss " \ + "property array is full"); \ + (props)[num].name = (pname); \ + (props)[num].value = (pvalue); \ + (num)++; \ + } while (0) + +/** + * Requests the KMS plugin to generate a key of the specified key type, size + * and mode, and stores the secure key blob into the specified file. + * + * For an XTS-mode key, 2 keys are generated and those are cross linked using + * its properties. + * + * The key ID(s) and label(s) of the generated keys (2 for XTS) are set into + * the properties object. + * + * @param[in] kms_info information of the currently bound plugin. + * @param[in] name the name of the key to generate + * @param[in] key_type the key type o the key to generate + * @param[in] key_props a properties object containing the key's initial + * properties. On return additional properties are + * added/set for the key ID(s) and label(s). + * @param[in] xts if true, an XTS key (i.e 2 keys) is generated + * @param[in] keybits the key bit size (e.g. 128, 196, 256, 0 to use the + * plugin's default) + * @param[in] filename the file name to store the key in + * @param[in] kms_options an array of KMS options specified, or NULL if no + * KMS options have been specified + * @param[in] num_kms_options the number of options in above array + * @param[in] verbose if true, verbose messages are printed + * + * @returns 0 for success or a negative errno in case of an error. + * If the KMS plugin does not support the key type, then -ENOTSUP is returned + */ +int generate_kms_key(struct kms_info *kms_info, const char *name, + const char *key_type, struct properties *key_props, + bool xts, size_t keybits, const char *filename, + struct kms_option *kms_options, size_t num_kms_options, + bool verbose) +{ + char *cipher, *iv_mode, *description, *volumes, *vol_type, *sector_size; + unsigned char key_blob[MAX_SECURE_KEY_SIZE * 2]; + char key1_label[KMS_KEY_LABEL_SIZE + 1] = { 0 }; + char key2_label[KMS_KEY_LABEL_SIZE + 1] = { 0 }; + char key1_id[KMS_KEY_ID_SIZE + 1] = { 0 }; + char key2_id[KMS_KEY_ID_SIZE + 1] = { 0 }; + struct kms_property kms_props[12]; + int xts_mode_prop = -1, rc = 0; + size_t key_size, key_blob_size; + enum kms_key_mode key_mode; + size_t num_kms_props = 0; + char *sys_volumes = NULL; + + util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); + util_assert(name != NULL, "Internal error: name is NULL"); + util_assert(key_type != NULL, "Internal error: key_type is NULL"); + util_assert(key_props != NULL, "Internal error: key_props is NULL"); + util_assert(filename != NULL, "Internal error: filename is NULL"); + + if (kms_info->plugin_lib == NULL) { + warnx("The repository is not bound to a KMS plugin"); + return -ENOENT; + } + + if (kms_info->funcs->kms_generate_key == NULL || + kms_info->funcs->kms_set_key_properties == NULL) { + pr_verbose(verbose, "The KMS plugin does not support to " + "generate keys or set properties"); + return -ENOTSUP; + } + + if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0) + key_size = AESDATA_KEY_SIZE; + else if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) + key_size = AESCIPHER_KEY_SIZE; + else if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) + key_size = EP11_KEY_SIZE; + else + return -ENOTSUP; + + memset(key_blob, 0, sizeof(key_blob)); + + cipher = properties_get(key_props, PROP_NAME_CIPHER); + iv_mode = properties_get(key_props, PROP_NAME_IV_MODE); + description = properties_get(key_props, PROP_NAME_DESCRIPTION); + volumes = properties_get(key_props, PROP_NAME_VOLUMES); + vol_type = properties_get(key_props, PROP_NAME_VOLUME_TYPE); + sector_size = properties_get(key_props, PROP_NAME_SECTOR_SIZE); + + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_NAME, name); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_CIPHER, + cipher != NULL ? cipher : ""); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_IV_MODE, + iv_mode != NULL ? iv_mode : ""); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_DESCRIPTION, + description != NULL ? description : ""); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_VOLUMES, + volumes != NULL ? volumes : ""); + sys_volumes = _get_system_specific_prop_name(KMS_KEY_PROP_VOLUMES); + if (sys_volumes == NULL) + return -ENOMEM; + ADD_KMS_PROPS(kms_props, num_kms_props, sys_volumes, + volumes != NULL ? volumes : ""); + + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_VOLUME_TYPE, + vol_type != NULL ? vol_type : ""); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_SECTOR_SIZE, + sector_size != NULL ? sector_size : ""); + + if (xts) { + xts_mode_prop = num_kms_props; + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_XTS_KEY, + "XTS-KEY-1"); + } + + key_mode = xts ? KMS_KEY_MODE_XTS_1 : KMS_KEY_MODE_NON_XTS; + + key_blob_size = key_size; + rc = kms_info->funcs->kms_generate_key(kms_info->handle, key_type, + keybits, key_mode, + kms_props, num_kms_props, + kms_options, num_kms_options, + key_blob, &key_blob_size, + key1_id, sizeof(key1_id), + key1_label, sizeof(key1_label)); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to generate key #1: %s", + strerror(-rc)); + goto out; + } + + pr_verbose(verbose, "Key1: ID: '%s' Label: '%s'", key1_id, key1_label); + pr_verbose(verbose, "Keyblob #1: %lu bytes:'", key_blob_size); + if (verbose) + util_hexdump_grp(stderr, NULL, key_blob, 4, key_blob_size, 0); + + /* Save ID and label of 1st key */ + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID : + PROP_NAME_KMS_KEY_ID, key1_id); + if (rc != 0) { + pr_verbose(verbose, "Failed to set key id of key #1: %s", + strerror(-rc)); + goto out; + } + + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_LABEL : + PROP_NAME_KMS_KEY_LABEL, key1_label); + if (rc != 0) { + pr_verbose(verbose, "Failed to set key label of key #1: %s", + strerror(-rc)); + goto out; + } + + if (!xts) + goto save_key; + + /* Generate 2nd key of the XTS key */ + kms_props[xts_mode_prop].value = "XTS-KEY-2"; + key_mode = KMS_KEY_MODE_XTS_2; + + /* Cross link key 1 with key 2 */ + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_XTS_KEY1_ID, + key1_id); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_XTS_KEY1_LABEL, + key1_label); + + key_blob_size = key_size; + rc = kms_info->funcs->kms_generate_key(kms_info->handle, key_type, + keybits, key_mode, + kms_props, num_kms_props, + kms_options, num_kms_options, + &key_blob[key_size], + &key_blob_size, + key2_id, sizeof(key2_id), + key2_label, sizeof(key2_label)); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to generate key #2: %s", + strerror(-rc)); + goto out; + } + + pr_verbose(verbose, "Key2: ID: '%s' Label: '%s'", key1_id, key1_label); + pr_verbose(verbose, "Keyblob #2: %lu bytes:'", key_blob_size); + if (verbose) + util_hexdump_grp(stderr, NULL, &key_blob[key_size], 4, + key_blob_size, 0); + + /* Save ID and label of 2nd key */ + rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_ID, key2_id); + if (rc != 0) { + pr_verbose(verbose, "Failed to set key id of key #2: %s", + strerror(-rc)); + goto out; + } + + rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_LABEL, + key2_label); + if (rc != 0) { + pr_verbose(verbose, "Failed to set key label of key #2: %s", + strerror(-rc)); + goto out; + } + + /* Cross link key 2 with key 1 */ + num_kms_props = 0; + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_XTS_KEY2_ID, + key2_id); + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_XTS_KEY2_LABEL, + key2_label); + + rc = kms_info->funcs->kms_set_key_properties(kms_info->handle, key1_id, + kms_props, num_kms_props); + if (rc != 0) { + pr_verbose(verbose, "KMS plugin failed to set properties of " + "key #1: %s", strerror(-rc)); + goto out; + } + +save_key: + rc = write_secure_key(filename, key_blob, xts ? key_size * 2 : key_size, + verbose); + if (rc != 0) + goto out; + +out: + if (cipher != NULL) + free(cipher); + if (iv_mode != NULL) + free(iv_mode); + if (description != NULL) + free(description); + if (volumes != NULL) + free(volumes); + if (vol_type != NULL) + free(vol_type); + if (sector_size != NULL) + free(sector_size); + if (sys_volumes != NULL) + free(sys_volumes); + + return rc; +} + diff --git a/zkey/kms.h b/zkey/kms.h index 04f0628d..51dfb8a5 100644 --- a/zkey/kms.h +++ b/zkey/kms.h @@ -66,4 +66,15 @@ int reencipher_kms(struct kms_info *kms_info, bool from_old, bool to_new, struct kms_option *kms_options, size_t num_kms_options, bool verbose); +int perform_kms_login(struct kms_info *kms_info, bool verbose); + +int get_kms_apqns_for_key_type(struct kms_info *kms_info, const char *key_type, + bool cross_check, char **apqns, bool verbose); + +int generate_kms_key(struct kms_info *kms_info, const char *name, + const char *key_type, struct properties *key_props, + bool xts, size_t keybits, const char *filename, + struct kms_option *kms_options, size_t num_kms_options, + bool verbose); + #endif diff --git a/zkey/pkey.c b/zkey/pkey.c index 83e3b3fb..e2aec9e8 100644 --- a/zkey/pkey.c +++ b/zkey/pkey.c @@ -45,8 +45,6 @@ #define MAX_CIPHER_LEN 32 -#define DEFAULT_KEYBITS 256 - #define INITIAL_APQN_ENTRIES 16 /** diff --git a/zkey/pkey.h b/zkey/pkey.h index 93c48aff..35392b8d 100644 --- a/zkey/pkey.h +++ b/zkey/pkey.h @@ -263,6 +263,7 @@ struct pkey_apqns4keytype { #define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER" #define KEY_TYPE_EP11_AES "EP11-AES" +#define DEFAULT_KEYBITS 256 #define PAES_BLOCK_SIZE 16 #define ENC_ZERO_LEN (2 * PAES_BLOCK_SIZE) #define VERIFICATION_PATTERN_LEN (2 * ENC_ZERO_LEN + 1) diff --git a/zkey/zkey.1 b/zkey/zkey.1 index 5dfa99e4..0d9e957f 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -125,25 +125,36 @@ key repository. .IR clear\-key\-file ] .RB [ \-\-key-type | \-K .IR type ] +.RB [ \-\-local | \-L ] +.RB [ KMS\-plugin\ specific\ options ] .RB [ \-\-verbose | \-V ] .PP Use the .B generate command to generate a new secure AES key either randomly within the CCA or EP11 -cryptographic adapter, or from a clear AES key specified as input. When specifying -a clear key as input, the clear key should be kept in a secure place, or be -securely erased after creation of the secure key. The secure key itself does -not need to be kept secure, because it can only be used together with a -CCA or EP11 cryptographic adapter that contains the master key with which the -secure key was generated. +cryptographic adapter, from a clear AES key specified as input, or using a key +management system plugin (KMS plugin). When specifying a clear key as input, the +clear key should be kept in a secure place, or be securely erased after creation +of the secure key. The secure key itself does not need to be kept secure, +because it can only be used together with a CCA or EP11 cryptographic adapter +that contains the master key with which the secure key was generated. +.PP +When the secure key repository is bound to a key management system plugin (KMS +plugin), then the secure key is generated by using the key management system, +except the \fB\-\-local\fP option is specified. +.PP +A key management system plugin may offer plugin specific options that can be +specified with the \fBgenerate\fP command. Use \fBgenerate \-\-help\fP +to display the plugin specific options and their meaning. .PP The generated secure key can either be stored in a file in the file system, or in the secure key repository. To store the generated secure key in a file, specify the file name with option \fIsecure\-key\-file\fP. To store the secure key in the secure key repository, specify the name of the key using the .B \-\-name -option. When storing the secure key in a key repository, -additional information can be associated with a secure key using the +option. Secure keys generated using a key management system plugin can only be +stored in a secure key repository. When storing the secure key in a key +repository, additional information can be associated with a secure key using the .B \-\-description , .B \-\-volumes @@ -151,13 +162,18 @@ additional information can be associated with a secure key using the .B \-\-apqns , or the .B \-\-sector-size -options. +options. When the secure key repository is bound to a key management system +plugin, then you can not associate specific APQNs with such keys, but the keys +inherit the APQNs that are associated with the key management system plugin. .PP You can generate different types of secure keys: \fBCCA-AESDATA\fP keys, \fBCCA-AESCIPHER\fP, and \fBEP11-AES\fP keys. Specify the type of the secure key using the .B \-\-key\-type -option. The default key type is CCA-AESDATA. +option. Normally, the default key type is CCA-AESDATA. If the secure key +repository is bound to a key management system plugin, and the plugin does not +support keys of type CCA-AESDATA, then the default key type is CCA-AESCIPHER, or +EP11-AES, whichever the plugin supports. .PP .B Note: Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic @@ -1013,6 +1029,8 @@ determines the size of the AES key. If option \fB\-\-keybits\fP is specified, the size of the specified file must match the specified key size. Valid file sizes are of 16, 24, or 32 bytes, and of 32 or 64 bytes for keys to be used with the XTS cipher mode. +When the secure key is generated using a key management system, then this option +can not be specified. .TP .BR \-N ", " \-\-name\~\fIkey-name\fP Specifies the name of the secure key in the secure key repository. @@ -1039,6 +1057,8 @@ then the first online APQN is used to generate the key. If no APQNs are specified, then an APQN is selected automatically. All specified APQNs must be online, unless the \fB\-\-no\-apqn\-check\fP option is specified. This option is only used for secure keys contained in the secure key repository. +When the secure key is generated using a key management system, then this option +can not be specified. .TP .BR \-\-no\-apqn\-check Do not check if the specified APQNs are available. Use this option to @@ -1068,6 +1088,18 @@ Secure keys of type \fBCCA-AESCIPHER\fP require an IBM cryptographic adapter in CCA coprocessor mode of version 6 or later, e.g. a CEX6C. Secure keys of type \fBEP11-AES\fP require an IBM cryptographic adapter in EP11 coprocessor mode of version 7 or later, e.g. a CEX7P. +.TP +.BR \-L ", "\-\-local +Generate the secure AES key locally. This is the default when no key management +system plugin (KMS plugin) is bound to the secure key repository. If the +repository is bound to a key management system plugin, then keys are generated +using the key management system by default. +.TP +.B KMS-plugin specific options +A key management system plugin may offer and even require plugin specific +options that can be specified with the generate command when the secure key +repository is bound to a key management system plugin. Use \fBgenerate +\-\-help\fP to display the plugin specific options and their meaning. . . . diff --git a/zkey/zkey.c b/zkey/zkey.c index 4cb65eb8..ca3f218d 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -255,6 +255,14 @@ static struct util_opt opt_vec[] = { "omitted, the default is '"KEY_TYPE_CCA_AESDATA"'", .command = COMMAND_GENERATE, }, + { + .option = { "local", 0, NULL, 'L'}, + .desc = "Generate the key locally. This is the default when no " + "KMS plugin is bound to the repository. If the " + "repository is bound to a KMS plugin, then keys are " + "generated by the KMS per default.", + .command = COMMAND_GENERATE, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -976,6 +984,7 @@ struct zkey_command { int need_keystore; int use_kms_plugin; char *kms_plugin_opts_cmd; + int need_kms_login; struct zkey_command *sub_commands; }; @@ -1090,6 +1099,8 @@ static struct zkey_command zkey_commands[] = { .pos_arg_optional = 1, .pos_arg_alternate = "--name/-N", .arg_alternate_value = &g.name, + .use_kms_plugin = 1, + .kms_plugin_opts_cmd = KMS_COMMAND_GENERATE, }, { .command = COMMAND_REENCIPHER, @@ -1392,11 +1403,44 @@ static int command_generate_repository(void) if (g.sector_size < 0) g.sector_size = 0; + if (g.kms_info.plugin_lib != NULL && !g.local) { + if (g.apqns != NULL) { + warnx("Option '--apqns|-a' is not valid for " + "generating a key in a KMS-bound repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + + if (g.clearkeyfile != NULL) { + warnx("Option '----clearkey|-c' is not valid for " + "generating a key in a KMS-bound repository, " + "unless option '--local|-L' is also specified"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + + rc = perform_kms_login(&g.kms_info, g.verbose); + if (rc != 0) + rc = EXIT_FAILURE; + + rc = keystore_generate_key_kms(g.keystore, g.name, + g.description, g.volumes, + g.sector_size, g.keybits, g.xts, + g.volume_type, g.key_type, + g.kms_options, + g.num_kms_options); + goto out; + } + + if (g.key_type == NULL) + g.key_type = KEY_TYPE_CCA_AESDATA; + rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.keybits, g.xts, g.clearkeyfile, g.volume_type, g.key_type, g.pkey_fd); +out: return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -1422,10 +1466,10 @@ static int command_generate(void) util_prg_print_parse_error(); return EXIT_FAILURE; } - if (g.key_type == NULL) - g.key_type = KEY_TYPE_CCA_AESDATA; if (g.name != NULL) return command_generate_repository(); + if (g.key_type == NULL) + g.key_type = KEY_TYPE_CCA_AESDATA; if (g.pos_arg != NULL) { if (g.volumes != NULL) { warnx("Option '--volumes|-l' is not valid for " @@ -1451,6 +1495,12 @@ static int command_generate(void) util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.local) { + warnx("Option '--local|-L' is not valid for " + "generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } rc = cross_check_apqns(NULL, NULL, get_min_card_level_for_keytype(g.key_type), @@ -2767,6 +2817,14 @@ int main(int argc, char *argv[]) rc = EXIT_FAILURE; goto out; } + + if (cmd->need_kms_login) { + rc = perform_kms_login(&g.kms_info, g.verbose); + if (rc != 0) { + rc = EXIT_FAILURE; + goto out; + } + } } umask(0077);