SHA256
1
0
forked from pool/s390-tools
Ana Guerrero 2025-01-30 13:52:54 +00:00 committed by Git OBS Bridge
commit 8474887a05
10 changed files with 2958 additions and 2 deletions

View File

@ -0,0 +1,299 @@
From 8c4b2872b8e24c1a27d8201beb5979c66ac05268 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 15 Feb 2024 09:08:43 +0100
Subject: [PATCH] zkey: Add support for retrieving a list of ultravisor secrets
Add functions to interface with the ultravisor device (/dev/uv) when
running in a secure execution guest to retrieve a list of available
secrets.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/Makefile | 4 +-
zkey/pvsecrets.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++
zkey/pvsecrets.h | 89 ++++++++++++++++++++++++++
3 files changed, 253 insertions(+), 1 deletion(-)
create mode 100644 zkey/pvsecrets.c
create mode 100644 zkey/pvsecrets.h
diff --git a/zkey/Makefile b/zkey/Makefile
index 501c5ebf..cbecf125 100644
--- a/zkey/Makefile
+++ b/zkey/Makefile
@@ -92,9 +92,11 @@ keystore.o: keystore.c keystore.h properties.h pkey.h cca.h ep11.h utils.h
zkey-cryptsetup.o: check-dep-zkey-cryptsetup zkey-cryptsetup.c pkey.h cca.h \
ep11.h misc.h utils.h
kms.o: kms.c kms.h kms-plugin.h utils.h pkey.h
+pvsecrets.o: pvsecrets.h
zkey: LDLIBS = -ldl -lcrypto
-zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o $(libs)
+zkey: zkey.o pkey.o cca.o ep11.o properties.o keystore.o utils.o kms.o \
+ pvsecrets.o $(libs)
$(LINK) $(ALL_LDFLAGS) $^ $(LDLIBS) -o $@
zkey-cryptsetup: LDLIBS = -ldl -lcryptsetup -ljson-c -lcrypto
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
new file mode 100644
index 00000000..2874fdf1
--- /dev/null
+++ b/zkey/pvsecrets.c
@@ -0,0 +1,161 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * Copyright IBM Corp. 2024
+ *
+ * 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 <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdbool.h>
+#include <string.h>
+#include <stdint.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <unistd.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 "pvsecrets.h"
+
+#define pr_verbose(verbose, fmt...) do { \
+ if (verbose) \
+ warnx(fmt); \
+ } while (0)
+
+/**
+ * Opens the ultravisor device and returns its file descriptor.
+ * This only succeeds when running in a secure execution guest.
+ * A failure of this function indicates that it is not running in a secure
+ * execution guest.
+ *
+ * @param verbose if true, verbose messages are printed
+ *
+ * @returns the file descriptor or -1 to indicate an error
+ */
+int uv_open_device(bool verbose)
+{
+ unsigned int pvguest = 0, max_retr_secrets = 0;
+ char *path = NULL;
+ int uv_fd, err;
+
+ uv_fd = open(UVDEVICE, O_RDWR);
+ if (uv_fd < 0) {
+ err = errno;
+ warnx("File '%s:' %s\n", UVDEVICE, strerror(errno));
+ if (err == EACCES)
+ warnx("Only the 'root' user is allowed to perform "
+ "this command");
+ else
+ warnx("Ensure that you are running in a secure "
+ "execution guest, and that the 'uvdevice' "
+ "kernel module is loaded.");
+ return -1;
+ }
+
+ path = util_path_sysfs(SYSFS_UV);
+ if (util_file_read_ui(&pvguest, 10, SYSFS_UV_PV_GUEST, path) != 0 ||
+ pvguest != 1) {
+ warnx("You are not running in a secure execution guest.");
+ goto error;
+ }
+
+ if (util_file_read_ui(&max_retr_secrets, 10, SYSFS_UV_MAX_SECRETS,
+ path) != 0 ||
+ max_retr_secrets == 0) {
+ warnx("The ultravisor device is at a too old version, or "
+ "the ultravisor does not support retrievable secrets.");
+ goto error;
+ }
+ free(path);
+
+ pr_verbose(verbose, "Device '%s' has been opened successfully",
+ UVDEVICE);
+ return uv_fd;
+
+error:
+ free(path);
+ close(uv_fd);
+
+ return -1;
+}
+
+/**
+ * Retrieves a list of secrets from the ultravisor. Calls the supplied callback
+ * function for each secret found.
+ *
+ * @param uv_fd the file descriptor of the ultravisor device
+ * @param cb the callback function
+ * @param cb_private private data to pass to the callback function
+ * @param verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
+ const u8 id[UV_SECRET_ID_LEN],
+ void *cb_private),
+ void *cb_private, bool verbose)
+{
+ struct uvio_list_secrets *list;
+ struct uvio_ioctl_cb io;
+ unsigned int i;
+ int rc;
+
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
+ util_assert(cb != NULL, "Internal error: cb is NULL");
+
+ list = util_zalloc(UVIO_LIST_SECRETS_MAX_LEN);
+
+ memset(&io, 0, sizeof(io));
+ io.argument_addr = list;
+ io.argument_len = UVIO_LIST_SECRETS_MAX_LEN;
+
+ rc = ioctl(uv_fd, UVIO_IOCTL_LIST_SECRETS, &io);
+ if (rc != 0) {
+ rc = -errno;
+
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS: %s",
+ strerror(-rc));
+
+ if (rc == -ENOTTY || rc == -EINVAL)
+ warnx("The ultravisor device is at a too old version");
+
+ goto out;
+ }
+
+ if (io.uv_rc != UVIO_RC_SUCCESS) {
+ pr_verbose(verbose, "ioctl UVIO_IOCTL_LIST_SECRETS' uv_rc: %u",
+ io.uv_rc);
+ rc = -EIO;
+ goto out;
+ }
+
+ pr_verbose(verbose, "Number of secrets: %u", list->num_secrets_stored);
+
+ for (i = 0; i < list->num_secrets_stored &&
+ i < ARRAY_SIZE(list->secret_entries); i++) {
+ if (list->secret_entries[i].secret_type <=
+ UV_SECRET_TYPE_AP_ASSOCIATION)
+ continue;
+
+ rc = cb(list->secret_entries[i].secret_idx,
+ list->secret_entries[i].secret_type,
+ list->secret_entries[i].secret_len,
+ list->secret_entries[i].secret_id,
+ cb_private);
+ if (rc != 0)
+ break;
+ }
+
+out:
+ free(list);
+
+ return rc;
+}
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
new file mode 100644
index 00000000..2667e859
--- /dev/null
+++ b/zkey/pvsecrets.h
@@ -0,0 +1,89 @@
+/*
+ * zkey - Generate, re-encipher, and validate secure keys
+ *
+ * This header file defines functions for the PV secrets support as well
+ * as the interface to the uv kernel module.
+ *
+ * Copyright IBM Corp. 2024
+ *
+ * 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 PVSECRETS_H
+#define PVSECRETS_H
+
+#include "lib/zt_common.h"
+
+/*
+ * Definitions for the /dev/uv kernel module interface
+ */
+#define UVDEVICE "/dev/uv"
+#define SYSFS_UV "firmware/uv"
+#define SYSFS_UV_PV_GUEST "%s/prot_virt_guest"
+#define SYSFS_UV_MAX_SECRETS "%s/query/max_retr_secrets"
+
+struct uvio_ioctl_cb {
+ u32 flags;
+ u16 uv_rc; /* UV header rc value */
+ u16 uv_rrc; /* UV header rrc value */
+ void *argument_addr; /* Userspace address of uvio argument */
+ u32 argument_len;
+ u8 reserved14[0x40 - 0x14]; /* must be zero */
+};
+
+#define UVIO_IOCTL_LIST_SECRETS_NR 3
+
+#define UVIO_TYPE_UVC 'u'
+#define UVIO_IOCTL(nr) _IOWR(UVIO_TYPE_UVC, \
+ nr, struct uvio_ioctl_cb)
+#define UVIO_IOCTL_LIST_SECRETS UVIO_IOCTL( \
+ UVIO_IOCTL_LIST_SECRETS_NR)
+
+#define UVIO_RC_SUCCESS 0x0001
+#define UVIO_RC_MORE_DATA 0x0100
+
+#define UV_SECRET_TYPE_INVALID 0x00
+#define UV_SECRET_TYPE_NULL 0x01
+#define UV_SECRET_TYPE_AP_ASSOCIATION 0x02
+#define UV_SECRET_TYPE_PLAIN_TEXT 0x03
+#define UV_SECRET_TYPE_AES_128 0x04
+#define UV_SECRET_TYPE_AES_192 0x05
+#define UV_SECRET_TYPE_AES_256 0x06
+#define UV_SECRET_TYPE_AES_XTS_128 0x07
+#define UV_SECRET_TYPE_AES_XTS_256 0x08
+#define UV_SECRET_TYPE_HMAC_SHA_256 0x09
+#define UV_SECRET_TYPE_HMAC_SHA_512 0x0a
+#define UV_SECRET_TYPE_ECDSA_P256 0x11
+#define UV_SECRET_TYPE_ECDSA_P384 0x12
+#define UV_SECRET_TYPE_ECDSA_P521 0x13
+#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
+#define UV_SECRET_TYPE_EDDSA_ED448 0x15
+
+#define UV_SECRET_ID_LEN 32
+
+#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
+
+struct uvio_list_secret_entry {
+ u16 secret_idx;
+ u16 secret_type;
+ u32 secret_len;
+ u64 reserved;
+ u8 secret_id[UV_SECRET_ID_LEN];
+} __packed;
+
+#define UVIO_MAX_SECRET_ENTRIES ((UVIO_LIST_SECRETS_MAX_LEN - 16) / \
+ sizeof(struct uvio_list_secret_entry))
+
+struct uvio_list_secrets {
+ u16 num_secrets_stored;
+ u16 num_secrets_total;
+ u16 next_secret_idx;
+ u16 reserved1;
+ u64 reserved2;
+ struct uvio_list_secret_entry secret_entries[UVIO_MAX_SECRET_ENTRIES];
+} __packed;
+
+int uv_open_device(bool verbose);
+
+#endif

View File

@ -0,0 +1,823 @@
From 5ce79ea667ea946e6591fe898db13becad018667 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 15 Feb 2024 11:22:04 +0100
Subject: [PATCH] zkey: Add the 'pvsecrets list' command
The 'pvsecrets list' command lists the available protected virtualization
secrets. By default, only those pvsecret types are listed, that can be used
with zkey. If option '--all/-a' is specified, then all pvsecret types are
listed. Nevertheless, pvsecret types not supported by zkey can not be used
with zkey.
This command only works when running in a secure execution guest.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/pvsecrets.c | 393 +++++++++++++++++++++++++++++++++++++++++++++++
zkey/pvsecrets.h | 4 +
zkey/zkey.1 | 91 ++++++++++-
zkey/zkey.c | 135 +++++++++++++++-
4 files changed, 619 insertions(+), 4 deletions(-)
diff --git a/zkey/pvsecrets.c b/zkey/pvsecrets.c
index 2874fdf1..7f28feba 100644
--- a/zkey/pvsecrets.c
+++ b/zkey/pvsecrets.c
@@ -7,6 +7,7 @@
* it under the terms of the MIT license. See LICENSE for details.
*/
+#include <ctype.h>
#include <err.h>
#include <errno.h>
#include <fcntl.h>
@@ -17,14 +18,60 @@
#include <sys/types.h>
#include <unistd.h>
+#include <openssl/sha.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 "pvsecrets.h"
+struct pvsecret_type_info {
+ u16 type;
+ const char *name;
+ bool zkey_usage;
+};
+
+static const struct pvsecret_type_info pvsecret_type_info[] = {
+ { .type = UV_SECRET_TYPE_NULL, .name = "NULL",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_AP_ASSOCIATION, .name = "AP-ASSOCIATION",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_PLAIN_TEXT, .name = "PLAIN-TEXT",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_AES_128, .name = "AES-128",
+ .zkey_usage = true },
+ { .type = UV_SECRET_TYPE_AES_192, .name = "AES-192",
+ .zkey_usage = true },
+ { .type = UV_SECRET_TYPE_AES_256, .name = "AES-256",
+ .zkey_usage = true },
+ { .type = UV_SECRET_TYPE_AES_XTS_128, .name = "AES-XTS-128",
+ .zkey_usage = true },
+ { .type = UV_SECRET_TYPE_AES_XTS_256, .name = "AES-XTS-256",
+ .zkey_usage = true },
+ { .type = UV_SECRET_TYPE_HMAC_SHA_256, .name = "HMAC-SHA-256",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_HMAC_SHA_512, .name = "HMAC-SHA-512",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_ECDSA_P256, .name = "ECDSA-P256",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_ECDSA_P384, .name = "ECDSA-P384",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_ECDSA_P521, .name = "ECDSA-P521",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_EDDSA_ED25519, .name = "EDDSA-ED25519",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_EDDSA_ED448, .name = "EDDSA-ED448",
+ .zkey_usage = false },
+ { .type = UV_SECRET_TYPE_INVALID, }
+};
+
+#define PVSECRETS_REC_ID "Secret ID"
+#define PVSECRETS_REC_TYPE "Type"
+
#define pr_verbose(verbose, fmt...) do { \
if (verbose) \
warnx(fmt); \
@@ -159,3 +206,349 @@ static int uv_list_secrets(int uv_fd, int (*cb)(u16 idx, u16 type, u32 len,
return rc;
}
+
+/**
+ * Returns true if the secret type is supported by zkey
+ *
+ * @param type the secret type
+ *
+ * @returns true if the type is supported, false otherwise
+ */
+static bool is_pvsecret_type_supported(u16 type)
+{
+ unsigned int i;
+
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
+ if (pvsecret_type_info[i].type == type)
+ return pvsecret_type_info[i].zkey_usage;
+ }
+
+ return false;
+}
+
+/**
+ * Returns the secret type name for the specified secret type
+ *
+ * @param type the secret type
+ *
+ * @returns a constant string containing the type name
+ */
+static const char *get_pvsecret_type_name(u16 type)
+{
+ unsigned int i;
+
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
+ if (pvsecret_type_info[i].type == type)
+ return pvsecret_type_info[i].name;
+ }
+
+ return "[UNKNOWN]";
+}
+
+/**
+ * Returns the secret type for the specified type name
+ *
+ * @param name the secret type name
+ *
+ * @returns the secret type or UV_SECRET_TYPE_INVALID if unknown.
+ */
+static u16 get_pvsecret_type_by_name(const char *name)
+{
+ unsigned int i;
+
+ for (i = 0; pvsecret_type_info[i].type != UV_SECRET_TYPE_INVALID; i++) {
+ if (strcasecmp(pvsecret_type_info[i].name, name) == 0)
+ return pvsecret_type_info[i].type;
+ }
+
+ return UV_SECRET_TYPE_INVALID;
+}
+
+/**
+ * Parses a 32 byte hex string into a 32 byte binary secret ID
+ *
+ * @param id_str the hex string to parse
+ * @param id the output buffer to store the secret ID
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+static int parse_secret_id_str(const char *id_str,
+ unsigned char id[UV_SECRET_ID_LEN])
+{
+ char hex[3] = { 0 };
+ unsigned long val;
+ unsigned int i;
+ char *endptr;
+
+ util_assert(id_str != NULL, "Internal error: id_str is NULL");
+ util_assert(id != NULL, "Internal error: id is NULL");
+
+ if (strncasecmp(id_str, "0x", 2) == 0)
+ id_str += 2;
+
+ if (strlen(id_str) != UV_SECRET_ID_LEN * 2)
+ return -EINVAL;
+
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
+ hex[0] = id_str[i * 2];
+ hex[1] = id_str[i * 2 + 1];
+
+ errno = 0;
+ val = strtoul(hex, &endptr, 16);
+ if (errno != 0 || *endptr != '\0' || val > 0xff)
+ return -EINVAL;
+
+ id[i] = val;
+ }
+
+ return 0;
+}
+
+/**
+ * Get the 32 byte binary secret ID from the secret name by calculating the
+ * SHA-256 has from the name.
+ *
+ * @param name the name of the secret
+ * @param id the output buffer to store the secret ID
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+static int get_secret_id_from_name(const char *name,
+ unsigned char id[UV_SECRET_ID_LEN])
+{
+ util_assert(name != NULL, "Internal error: id_str is NULL");
+ util_assert(id != NULL, "Internal error: id is NULL");
+ util_assert(UV_SECRET_ID_LEN == SHA256_DIGEST_LENGTH,
+ "Internal error: UV_SECRET_ID_LEN != SHA256_DIGEST_LENGTH");
+
+ if (SHA256((const unsigned char *)name, strlen(name), id) != id)
+ return -EIO;
+
+ return 0;
+}
+
+/**
+ * Gets the binary 32 byte secret id from either a hex string or a secret name.
+ *
+ * @param hex the secret id as hex string. Can be NULL.
+ * @param name the secret name. Can be NULL. If the id
+ * parameter is non-NULL, then this parameter is
+ * ignored.
+ * @param id Output: the 32 byte binary secret id.
+ * @param id_str Output: the secret id in printable ascii chars
+ * form, if name is non-NULL and the name length is
+ * less than UV_SECRET_ID_LEN.
+ *
+ * @returns 0 on success, a negative errno in case of an error.
+ * If neither the hex string nor the secret name is specified, 1 is returned,
+ * and the id parameter is not modified.
+ */
+static int get_secret_id_from_hex_or_name(const char *hex, const char *name,
+ unsigned char id[UV_SECRET_ID_LEN],
+ char id_name[UV_SECRET_ID_LEN])
+{
+ int rc;
+
+ util_assert(id != NULL, "Internal error: id is NULL");
+
+ if (hex != NULL) {
+ rc = parse_secret_id_str(hex, id);
+ if (rc != 0) {
+ warnx("Invalid pvsecret id specified: '%s'", hex);
+ return rc;
+ }
+
+ return 0;
+ }
+
+ if (name != NULL) {
+ rc = get_secret_id_from_name(name, id);
+ if (rc != 0) {
+ warnx("Failed to get the ID from pvsecret name: '%s'",
+ name);
+ return rc;
+ }
+
+ if (strlen(name) < UV_SECRET_ID_LEN) {
+ strncpy(id_name, name, UV_SECRET_ID_LEN);
+ id_name[UV_SECRET_ID_LEN - 1] = '\0';
+ }
+
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Checks if the secret id is printable. To be printable, all characters up to
+ * the first zero byte must be printable. All bytes after the first zero byte
+ * must be all zero. There must be at least one zero byte as the very last byte
+ * of the id.
+ *
+ * @param id the ID of the secret
+ * @param name Output: the id in the printable form and enclosed
+ * in single quotes if the id is printable. The max
+ * length of the name buffer is UV_SECRET_ID_LEN + 2:
+ * A starting quote, up to UV_SECRET_ID_LEN-1 chars,
+ * an ending quote and a zero termination byte.
+ *
+ * @returns true if the id is printable, false otherwise.
+ */
+static bool is_printable_name(const u8 id[UV_SECRET_ID_LEN],
+ char name[UV_SECRET_ID_LEN + 2])
+{
+ bool end_found = false, printable_name = false;
+ unsigned int i;
+
+ name[0] = '\'';
+ for (i = 0; i < UV_SECRET_ID_LEN; i++) {
+ if (!end_found) {
+ if (id[i] == '\0') {
+ name[1 + i] = '\'';
+ end_found = true;
+ } else if (isprint(id[i])) {
+ name[1 + i] = id[i];
+ printable_name = true;
+ } else {
+ printable_name = false;
+ end_found = true;
+ }
+ } else if (id[i] != '\0') {
+ printable_name = false;
+ }
+ }
+ if (!end_found)
+ printable_name = false;
+
+ return printable_name;
+}
+
+struct list_secrets_data {
+ struct util_rec *rec;
+ bool all;
+ bool hex;
+ u16 type_filter;
+ bool id_filter;
+ char name[UV_SECRET_ID_LEN];
+ unsigned char id[UV_SECRET_ID_LEN];
+ unsigned int matched;
+};
+
+/**
+ * Callback used with pvsecrets_list function. Called for each secret.
+ *
+ * @param idx the index of the secret
+ * @param type the type of the secret
+ * @param id the ID of the secret
+ * @param cb_private callback private data
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int pvsecrets_list_cb(u16 UNUSED(idx), u16 type, u32 UNUSED(len),
+ const u8 id[UV_SECRET_ID_LEN], void *cb_private)
+{
+ struct list_secrets_data *list_data = cb_private;
+ char name[2 + UV_SECRET_ID_LEN] = { 0 };
+ char hex[2 * UV_SECRET_ID_LEN + 1] = { 0 };
+ unsigned int i;
+
+ if (!list_data->all && !is_pvsecret_type_supported(type))
+ return 0;
+
+ if (list_data->type_filter != 0 && type != list_data->type_filter)
+ return 0;
+
+ if (list_data->id_filter &&
+ memcmp(id, list_data->name, UV_SECRET_ID_LEN) != 0 &&
+ memcmp(id, list_data->id, UV_SECRET_ID_LEN) != 0)
+ return 0;
+
+ for (i = 0; i < UV_SECRET_ID_LEN; i++)
+ sprintf(&hex[i * 2], "%02x", id[i]);
+
+ if (!list_data->hex && is_printable_name(id, name))
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, name);
+ else
+ util_rec_set(list_data->rec, PVSECRETS_REC_ID, hex);
+ util_rec_set(list_data->rec, PVSECRETS_REC_TYPE,
+ get_pvsecret_type_name(type));
+
+ if (list_data->matched == 0)
+ util_rec_print_hdr(list_data->rec);
+
+ util_rec_print(list_data->rec);
+
+ list_data->matched++;
+
+ return 0;
+}
+
+/**
+ * Lists protected virtualization secrets.
+ *
+ * @param uv_fd the file descriptor of the ultravisor device
+ * @param all if true, all secret types are listed
+ * @param hex if true, list the secret ID in hex, even if the
+ * secret ID would be printable
+ * @param type_filter only display secrets of the specified secret type.
+ * Can be NULL.
+ * @param secret_id the secret id to list. Can be NULL.
+ * @param secret_name the secret name to list. Can be NULL. If the id
+ * parameter is non-NULL, then this parameter is
+ * ignored.
+ * @param verbose if true, verbose messages are printed
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
+ const char *secret_id, const char *secret_name,
+ bool verbose)
+{
+ struct list_secrets_data list_data = { 0 };
+ int rc;
+
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
+
+ list_data.all = all;
+ list_data.hex = hex;
+ list_data.type_filter = UV_SECRET_TYPE_INVALID;
+
+ if (type_filter != NULL) {
+ list_data.type_filter = get_pvsecret_type_by_name(type_filter);
+ if (list_data.type_filter == UV_SECRET_TYPE_INVALID) {
+ warnx("Invalid pvsecret type specified: %s",
+ type_filter);
+ return -EINVAL;
+ }
+ }
+
+ if (secret_id != NULL || secret_name != NULL) {
+ rc = get_secret_id_from_hex_or_name(secret_id, secret_name,
+ list_data.id,
+ list_data.name);
+ if (rc < 0)
+ return rc;
+
+ list_data.id_filter = true;
+ }
+
+ list_data.rec = util_rec_new_wide("-");
+ util_rec_def(list_data.rec, PVSECRETS_REC_ID, UTIL_REC_ALIGN_LEFT,
+ UV_SECRET_ID_LEN * 2, PVSECRETS_REC_ID);
+ util_rec_def(list_data.rec, PVSECRETS_REC_TYPE, UTIL_REC_ALIGN_LEFT,
+ 12, PVSECRETS_REC_TYPE);
+
+ rc = uv_list_secrets(uv_fd, pvsecrets_list_cb, &list_data, verbose);
+ if (rc != 0) {
+ warnx("Failed to list protected virtualization secrets: %s",
+ strerror(-rc));
+ }
+
+ util_rec_free(list_data.rec);
+
+ if (list_data.matched == 0)
+ rc = -ENOENT;
+
+ return rc;
+}
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
index 2667e859..6acebfdd 100644
--- a/zkey/pvsecrets.h
+++ b/zkey/pvsecrets.h
@@ -86,4 +86,8 @@ struct uvio_list_secrets {
int uv_open_device(bool verbose);
+int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
+ const char *secret_id, const char *secret_name,
+ bool verbose);
+
#endif
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index 8c5a09a7..4386629f 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -1,8 +1,8 @@
-.\" Copyright IBM Corp. 2017, 2020
+.\" Copyright IBM Corp. 2017, 2024
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
-.TH ZKEY 1 "July 2020" "s390-tools"
+.TH ZKEY 1 "February 2024" "s390-tools"
.SH NAME
zkey \- Manage secure AES keys
.
@@ -1162,6 +1162,54 @@ fails. Use option \fB\-\-no\-volume\-check\fP to omit the volume check, and
refresh the keys even if the associated volume(s) do not exist.
.
.
+.SH COMMANDS FOR PROTECTED VIRTUALIZATION
+.
+Use the \fBpvsecrets\fP command to work with protected virtualization (PV)
+secrets. Protected virtualization secrets can be made available to a secure
+execution guest and can be used only within that guest.
+The \fBpvsecrets\fP command provides subcommands for protected
+virtualization specific operations. Use \fBzkey pvsecrets \-\-help\fP to show
+the available subcommands. These subcommands only work when running in a
+secure execution guest. Only the \fBroot\fP user is allowed to perform these
+subcommands.
+.
+.SS "List available protected virtualization secrets"
+.
+.B zkey pvsecrets
+.BR list | li
+.RB [ \-\-all | \-A ]
+.RB [ \-\-hex | \-H ]
+.RB [ \-\-pvsecret\-type | \-T
+.IR pvsecret\-type ]
+.RB [ \-\-pvsecret\-id | \-I
+.IR pvsecret\-id ]
+.RB [ \-\-pvsecret\-name | \-e
+.IR pvsecret\-name ]
+.RB [ \-\-verbose | \-V ]
+.
+.PP
+Use the
+.B pvsecrets list
+command to display a list of protected virtualization (PV) secrets. It displays
+the pvsecret ID as hex string of 32 bytes or as printable name enclosed in
+single quotes (\fB'\fP), if the pvsecret ID consists of only printable
+characters. Specify the \fB\-\-hex\fP option to list all pvsecret IDs as hex
+string. The
+.B pvsecrets list
+command also shows the pvsecret type of each secret.
+.PP
+You can filter the list of pvsecrets by pvsecret ID, pvsecret name and pvsecret
+type. Either the \fB\-\-pvsecret\-id\fP option or the
+\fB\-\-pvsecret\-name\fP option can be specified. By default the
+\fBpvsecrets list\fP command displays only those pvsecrets with types that
+are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the
+\fB\-\-all\fP option.
+.PP
+This command is only available when running in a secure execution guest.
+Only the \fBroot\fP user is allowed to perform this command.
+.
+.
+.
.
.SH OPTIONS
.SS "Options for the generate command"
@@ -2030,6 +2078,45 @@ repository. This option only has an effect when specified together with option
.
.
.
+.SS "Options for the pvsecrets list command"
+.TP
+.BR \-A ", " \-\-all
+List all protected virtualization (PV) secret types, not only those that can be
+used with zkey.
+.TP
+.BR \-H ", " \-\-hex
+Show all protected virtualization (PV) secret IDs in hex, even if the ID
+contains only printable characters.
+.TP
+.BR \-T ", " \-\-pvsecret\-type\~\fIpvsecret\-type\fP
+Type of the protected virtualization (PV) secret to list. If omitted, all
+secret types are listed. Possible values are: \fBPLAIN\-TEXT\fP, \fBAES\-128\fP,
+\fBAES\-192\fP, \fBAES\-256\fP, \fBAES\-XTS\-128\fP, \fBAES\-XTS\-256\fP,
+\fBHMAC\-SHA\-256\fP, \fBHMAC\-SHA\-512\fP, \fBECDSA\-P256\fP,
+\fBECDSA\-P384\fP, \fBECDSA\-P521\fP, \fBEDDSA\-ED25519\fP, and
+\fBEDDSA\-ED448\fP.
+.TP
+.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP
+ID of the protected virtualization (PV) secret to list. The pvsecret ID is a 32
+byte hex string, optionally prefixed by \fB0x\fP. You can use the YAML file that
+was created when using the \fBpvsecret create\fP command for adding the
+protected virtualization secret:
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP.
+You might have to install the \fByq\fP package first.
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
+can be specified, but not both.
+.TP
+.BR \-e ", " \-\-pvsecret\-name\~\fIpvsecret\-name\fP
+Name of the protected virtualization (PV) secret to list. You can use the YAML
+file that was created when using the \fBpvsecret create\fP command for adding
+the protected virtualization secret:
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
+You might have to install the \fByq\fP package first.
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
+can be specified, but not both.
+.
+.
+.
.SS "General options"
.TP
.BR \-V ", " \-\-verbose
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 7c909ff0..adc48d60 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -1,7 +1,7 @@
/*
* zkey - Generate, re-encipher, and validate secure keys
*
- * Copyright IBM Corp. 2017, 2020
+ * Copyright IBM Corp. 2017, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -34,6 +34,7 @@
#include "pkey.h"
#include "utils.h"
#include "kms.h"
+#include "pvsecrets.h"
/*
* Program configuration
@@ -46,7 +47,7 @@ static const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2017,
- .pub_last = 2020,
+ .pub_last = 2024,
},
UTIL_PRG_COPYRIGHT_END
}
@@ -93,10 +94,16 @@ static struct zkey_globals {
bool open;
bool format;
bool refresh_properties;
+ bool all;
+ bool hex;
+ char *secret_type;
+ char *secret_id;
+ char *secret_name;
struct ext_lib lib;
struct cca_lib cca;
struct ep11_lib ep11;
int pkey_fd;
+ int uv_fd;
struct keystore *keystore;
struct kms_info kms_info;
int first_kms_option;
@@ -104,6 +111,7 @@ static struct zkey_globals {
size_t num_kms_options;
} g = {
.pkey_fd = -1,
+ .uv_fd = -1,
.sector_size = -1,
.lib.cca = &g.cca,
.lib.ep11 = &g.ep11,
@@ -135,6 +143,8 @@ static struct zkey_globals {
#define COMMAND_KMS_LIST "list"
#define COMMAND_KMS_IMPORT "import"
#define COMMAND_KMS_REFRESH "refresh"
+#define COMMAND_PVSECRETS "pvsecrets"
+#define COMMAND_PVSECRETS_LIST "list"
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
@@ -1182,6 +1192,48 @@ static struct util_opt opt_vec[] = {
.flags = UTIL_OPT_FLAG_NOSHORT,
},
/***********************************************************/
+ {
+ .flags = UTIL_OPT_FLAG_SECTION,
+ .desc = "OPTIONS",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ {
+ .option = {"all", 0, NULL, 'A'},
+ .desc = "List all protected virtualization (PV) secret types, "
+ "not only those that can be used with zkey.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ {
+ .option = {"hex", 0, NULL, 'H'},
+ .desc = "Show all protected virtualization (PV) secret IDs in "
+ "hex, even if the ID contains only printable "
+ "characters.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ {
+ .option = { "pvsecret-type", required_argument, NULL, 'T'},
+ .argument = "PVSECRET-TYPE",
+ .desc = "Type of the protected virtualization (PV) secret to "
+ "list. If omitted, all pvsecret types are listed.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ {
+ .option = { "pvsecret-id", required_argument, NULL, 'I'},
+ .argument = "PVSECRET-ID",
+ .desc = "ID of the protected virtualization (PV) secret to "
+ "list. Either '--pvsecret-id/-I' or "
+ "'--pvsecret-name/-e' can be specified, but not both.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ {
+ .option = { "pvsecret-name", required_argument, NULL, 'e'},
+ .argument = "PVSECRET-NAME",
+ .desc = "Name of the protected virtualization (PV) secret to "
+ "list. Either '--pvsecret-name/-e' or "
+ "'--pvsecret-id/-I' can be specified, but not both.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
+ },
+ /***********************************************************/
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
@@ -1249,6 +1301,7 @@ struct zkey_command {
int need_cca_library;
int need_ep11_library;
int need_pkey_device;
+ int need_uv_device;
char *short_desc;
char *long_desc;
int has_options;
@@ -1285,6 +1338,7 @@ static int command_kms_reencipher(void);
static int command_kms_list(void);
static int command_kms_import(void);
static int command_kms_refresh(void);
+static int command_pvsecrets_list(void);
static struct zkey_command zkey_kms_commands[] = {
{
@@ -1402,6 +1456,22 @@ static struct zkey_command zkey_kms_commands[] = {
{ .command = NULL }
};
+static struct zkey_command zkey_pvsecrets_commands[] = {
+ {
+ .command = COMMAND_PVSECRETS_LIST,
+ .abbrev_len = 2,
+ .function = command_pvsecrets_list,
+ .short_desc = "Lists protected virtualization (PV) secrets",
+ .long_desc = "Lists available protected virtualization (PV) "
+ "secrets. This command is only available when "
+ "running in a secure execution guest. Only the "
+ "'root' user is allowed to perform this command",
+ .has_options = 1,
+ .need_uv_device = 1,
+ },
+ { .command = NULL }
+};
+
static struct zkey_command zkey_commands[] = {
{
.command = COMMAND_GENERATE,
@@ -1565,6 +1635,21 @@ static struct zkey_command zkey_commands[] = {
.has_options = 1,
.sub_commands = zkey_kms_commands,
},
+ {
+ .command = COMMAND_PVSECRETS,
+ .abbrev_len = 2,
+ .short_desc = "Protected virtualization (PV) support",
+ .long_desc = "Provides subcommands for working with protected "
+ "virtualization (PV) secrets. Protected "
+ "virtualization secrets can be made available to "
+ "a secure execution guest and can be used only "
+ "within that guest. Thus, these subcommands are "
+ "only available when running in a secure "
+ "execution guest. Only the 'root' user is allowed "
+ "to perform these subcommands.",
+ .has_options = 1,
+ .sub_commands = zkey_pvsecrets_commands,
+ },
{ .command = NULL }
};
@@ -2899,6 +2984,28 @@ static int command_kms_refresh(void)
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
+/*
+ * Command handler for 'pvsecrets list'.
+ *
+ * Lists available protected virtualization secrets
+ */
+static int command_pvsecrets_list(void)
+{
+ int rc;
+
+ if (g.secret_id != NULL && g.secret_name != NULL) {
+ warnx("Either '--pvsecret-id/-I' or '--pvsecret-name/-e' can "
+ "be specified, but not both");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
+ rc = pvsecrets_list(g.uv_fd, g.all, g.hex, g.secret_type, g.secret_id,
+ g.secret_name, g.verbose);
+
+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
/**
* Opens the keystore. The keystore directory is either the
* default directory or as specified in an environment variable
@@ -3234,6 +3341,21 @@ int main(int argc, char *argv[])
case OPT_REMOVE_DUMMY_PASSPHRASE:
g.remove_passphrase = 1;
break;
+ case 'A':
+ g.all = 1;
+ break;
+ case 'H':
+ g.hex = 1;
+ break;
+ case 'T':
+ g.secret_type = optarg;
+ break;
+ case 'I':
+ g.secret_id = optarg;
+ break;
+ case 'e':
+ g.secret_name = optarg;
+ break;
case 'h':
print_help(command, sub_command);
return EXIT_SUCCESS;
@@ -3294,6 +3416,13 @@ int main(int argc, char *argv[])
goto out;
}
}
+ if (cmd->need_uv_device) {
+ g.uv_fd = uv_open_device(g.verbose);
+ if (g.uv_fd == -1) {
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+ }
if (g.kms_info.plugin_lib != NULL) {
rc = init_kms_plugin(&g.kms_info, g.verbose);
@@ -3323,6 +3452,8 @@ int main(int argc, char *argv[])
dlclose(g.ep11.lib_ep11);
if (g.pkey_fd >= 0)
close(g.pkey_fd);
+ if (g.uv_fd >= 0)
+ close(g.uv_fd);
if (g.keystore)
keystore_free(g.keystore);
if (g.kms_options != NULL)

View File

@ -0,0 +1,340 @@
From fdf66dc148c09f1d3300cd3378e3f75a83c6214e Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 15 Feb 2024 16:56:04 +0100
Subject: [PATCH] zkey: Add PVSECRETS-AES key type
Add the definitions and utility functions for the PVSECRETS-AES key type.
A PVSECRETS-AES key token contains the secret id of a protected
virtualization secret. It does not contain the key material, just a
reference to the key in the ultravisor.
When such a key token is used to perform crypto operations later on, the
PAES kernel cipher will obtain the protected key belonging to this secret
id with the help of the pkey kernel module.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/keystore.c | 4 +-
zkey/pkey.c | 110 ++++++++++++++++++++++++++++++++++++++++++++++-
zkey/pkey.h | 38 ++++++++++++----
zkey/pvsecrets.h | 4 +-
4 files changed, 143 insertions(+), 13 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 9589d82d..771bc08d 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -3,7 +3,7 @@
*
* Keystore handling functions
*
- * Copyright IBM Corp. 2018, 2020
+ * Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -360,6 +360,8 @@ static int _keystore_valid_key_type(const char *key_type)
return 1;
if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
return 1;
+ if (strcasecmp(key_type, KEY_TYPE_PVSECRET_AES) == 0)
+ return 1;
return 0;
}
diff --git a/zkey/pkey.c b/zkey/pkey.c
index 821978bd..53c0a550 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -1,7 +1,7 @@
/*
* zkey - Generate, re-encipher, and validate secure keys
*
- * Copyright IBM Corp. 2018
+ * Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -26,6 +26,7 @@
#include "lib/util_panic.h"
#include "pkey.h"
+#include "pvsecrets.h"
#include "utils.h"
#ifndef AF_ALG
@@ -1719,6 +1720,38 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
}
}
+/**
+ * Check if the specified key is a PVSECRET-AES key token.
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns true if the key is a PVSECRET token type
+ */
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size)
+{
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
+
+ if (key == NULL || key_size < PVSECRET_KEY_SIZE)
+ return false;
+
+ if (pvsecret->hdr.type != TOKEN_TYPE_NON_CCA)
+ return false;
+ if (pvsecret->hdr.version != TOKEN_VERSION_PVSECRET)
+ return false;
+
+ switch (pvsecret->secret_type) {
+ case UV_SECRET_TYPE_AES_128:
+ case UV_SECRET_TYPE_AES_192:
+ case UV_SECRET_TYPE_AES_256:
+ case UV_SECRET_TYPE_AES_XTS_128:
+ case UV_SECRET_TYPE_AES_XTS_256:
+ return true;
+ default:
+ return false;
+ }
+}
+
/**
* Check if the specified key is an XTS type key
*
@@ -1729,6 +1762,8 @@ bool is_ep11_key_session_bound(const u8 *key, size_t key_size)
*/
bool is_xts_key(const u8 *key, size_t key_size)
{
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
+
if (is_cca_aes_data_key(key, key_size)) {
if (key_size == 2 * AESDATA_KEY_SIZE &&
is_cca_aes_data_key(key + AESDATA_KEY_SIZE,
@@ -1749,11 +1784,41 @@ bool is_xts_key(const u8 *key, size_t key_size)
is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE,
key_size - EP11_AES_KEY_SIZE))
return true;
+ } else if (is_pvsecret_aes_key(key, key_size)) {
+ switch (pvsecret->secret_type) {
+ case UV_SECRET_TYPE_AES_XTS_128:
+ case UV_SECRET_TYPE_AES_XTS_256:
+ return true;
+ default:
+ return false;
+ }
}
return false;
}
+/**
+ * Check if the specified key is a secure key type (i.e. requires a crypto card)
+ *
+ * @param[in] key the secure key token
+ * @param[in] key_size the size of the secure key
+ *
+ * @returns true if the key is a secure key type
+ */
+bool is_secure_key(const u8 *key, size_t key_size)
+{
+ if (is_cca_aes_data_key(key, key_size))
+ return true;
+ if (is_cca_aes_cipher_key(key, key_size))
+ return true;
+ if (is_ep11_aes_key(key, key_size))
+ return true;
+ if (is_ep11_aes_key_with_header(key, key_size))
+ return true;
+
+ return false;
+}
+
/**
* Gets the size in bits of the effective key of the specified secure key
*
@@ -1771,6 +1836,7 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key;
struct ep11keytoken *ep11key = (struct ep11keytoken *)key;
struct ep11kblob_header *hdr = (struct ep11kblob_header *)key;
+ struct pvsecrettoken *pvsecret = (struct pvsecrettoken *)key;
util_assert(bitsize != NULL, "Internal error: bitsize is NULL");
@@ -1805,6 +1871,26 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize)
(key + EP11_AES_KEY_SIZE);
*bitsize += hdr->bitlen;
}
+ } else if (is_pvsecret_aes_key(key, key_size)) {
+ switch (pvsecret->secret_type) {
+ case UV_SECRET_TYPE_AES_128:
+ *bitsize = 128;
+ break;
+ case UV_SECRET_TYPE_AES_192:
+ *bitsize = 192;
+ break;
+ case UV_SECRET_TYPE_AES_256:
+ *bitsize = 256;
+ break;
+ case UV_SECRET_TYPE_AES_XTS_128:
+ *bitsize = 128 * 2;
+ break;
+ case UV_SECRET_TYPE_AES_XTS_256:
+ *bitsize = 256 * 2;
+ break;
+ default:
+ return -EINVAL;
+ }
} else {
return -EINVAL;
}
@@ -1830,9 +1916,31 @@ const char *get_key_type(const u8 *key, size_t key_size)
return KEY_TYPE_EP11_AES;
if (is_ep11_aes_key_with_header(key, key_size))
return KEY_TYPE_EP11_AES;
+ if (is_pvsecret_aes_key(key, key_size))
+ return KEY_TYPE_PVSECRET_AES;
+
return NULL;
}
+/**
+ * Returns true if the key type is a secure key type
+ *
+ * @param[in] key_type the type of the key
+ *
+ * @returns true if the key type is a secure key type, false otherwise
+ */
+bool is_secure_key_type(const char *key_type)
+{
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0)
+ return true;
+ if (strcasecmp(key_type, KEY_TYPE_CCA_AESDATA) == 0)
+ return true;
+ if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0)
+ return true;
+
+ return false;
+}
+
/**
* Returns the minimum card level for a specific key type
*
diff --git a/zkey/pkey.h b/zkey/pkey.h
index 3b57c5f0..ce3bd8bd 100644
--- a/zkey/pkey.h
+++ b/zkey/pkey.h
@@ -4,7 +4,7 @@
* This header file defines the interface to the pkey kernel module.
* It defines a set of IOCTL commands with its associated structures.
*
- * Copyright IBM Corp. 2017, 2018
+ * Copyright IBM Corp. 2017, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -41,6 +41,8 @@ struct tokenheader {
#define TOKEN_VERSION_EP11_AES 0x03
#define TOKEN_VERSION_EP11_AES_WITH_HEADER 0x06
#define TOKEN_VERSION_EP11_ECC_WITH_HEADER 0x07
+/* 0x08 is reserved for internal use */
+#define TOKEN_VERSION_PVSECRET 0x09
struct aesdatakeytoken {
u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */
@@ -116,6 +118,15 @@ struct ep11keytoken {
u8 padding[64];
} __packed;
+#define UV_SECRET_ID_LEN 32
+
+struct pvsecrettoken {
+ struct tokenheader hdr;
+ u16 secret_type; /* the secret type as the UV told us */
+ u16 secret_len; /* length in bytes of the secret */
+ u8 secretid[UV_SECRET_ID_LEN]; /* the secret id for this secret */
+} __packed;
+
#define ZERO_SESSION \
"\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0"
@@ -124,21 +135,26 @@ struct ep11keytoken {
#define EP11_KEY_SIZE sizeof(struct ep11keytoken)
#define EP11_AES_KEY_SIZE (sizeof(struct ep11kblob_header) + \
sizeof(struct ep11keytoken))
+#define PVSECRET_KEY_SIZE sizeof(struct pvsecrettoken)
/* MAX/MIN from zt_common.h produces warnings for variable length arrays */
#define _MIN(a, b) ((a) < (b) ? (a) : (b))
#define _MAX(a, b) ((a) > (b) ? (a) : (b))
#define MAX_SECURE_KEY_SIZE _MAX( \
- _MAX(EP11_KEY_SIZE, \
- EP11_AES_KEY_SIZE), \
- _MAX(AESDATA_KEY_SIZE, \
- AESCIPHER_KEY_SIZE))
+ _MAX( \
+ _MAX(EP11_KEY_SIZE, \
+ EP11_AES_KEY_SIZE), \
+ _MAX(AESDATA_KEY_SIZE, \
+ AESCIPHER_KEY_SIZE)), \
+ PVSECRET_KEY_SIZE)
#define MIN_SECURE_KEY_SIZE _MIN( \
- _MIN(EP11_KEY_SIZE, \
- EP11_AES_KEY_SIZE), \
- _MIN(AESDATA_KEY_SIZE, \
- AESCIPHER_KEY_SIZE))
+ _MIN( \
+ _MIN(EP11_KEY_SIZE, \
+ EP11_AES_KEY_SIZE), \
+ _MIN(AESDATA_KEY_SIZE, \
+ AESCIPHER_KEY_SIZE)), \
+ PVSECRET_KEY_SIZE)
struct pkey_seckey {
u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */
@@ -285,6 +301,7 @@ struct pkey_apqns4keytype {
#define KEY_TYPE_CCA_AESDATA "CCA-AESDATA"
#define KEY_TYPE_CCA_AESCIPHER "CCA-AESCIPHER"
#define KEY_TYPE_EP11_AES "EP11-AES"
+#define KEY_TYPE_PVSECRET_AES "PVSECRET-AES"
#define DEFAULT_KEYBITS 256
#define PAES_BLOCK_SIZE 16
@@ -342,9 +359,12 @@ bool is_cca_aes_cipher_key(const u8 *key, size_t key_size);
bool is_ep11_aes_key(const u8 *key, size_t key_size);
bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size);
bool is_ep11_key_session_bound(const u8 *key, size_t key_size);
+bool is_pvsecret_aes_key(const u8 *key, size_t key_size);
bool is_xts_key(const u8 *key, size_t key_size);
+bool is_secure_key(const u8 *key, size_t key_size);
int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize);
const char *get_key_type(const u8 *key, size_t key_size);
+bool is_secure_key_type(const char *key_type);
int get_min_card_level_for_keytype(const char *key_type);
const struct fw_version *get_min_fw_version_for_keytype(const char *key_type);
enum card_type get_card_type_for_keytype(const char *key_type);
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
index 6acebfdd..ad844035 100644
--- a/zkey/pvsecrets.h
+++ b/zkey/pvsecrets.h
@@ -15,6 +15,8 @@
#include "lib/zt_common.h"
+#include "pkey.h"
+
/*
* Definitions for the /dev/uv kernel module interface
*/
@@ -60,8 +62,6 @@ struct uvio_ioctl_cb {
#define UV_SECRET_TYPE_EDDSA_ED25519 0x14
#define UV_SECRET_TYPE_EDDSA_ED448 0x15
-#define UV_SECRET_ID_LEN 32
-
#define UVIO_LIST_SECRETS_MAX_LEN 0x8000
struct uvio_list_secret_entry {

View File

@ -0,0 +1,784 @@
From 95bf7eb285f39a8f827cc013393cc69b1265cd68 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Thu, 15 Feb 2024 15:14:04 +0100
Subject: [PATCH] zkey: Add the 'pvsecrets import' command
The 'pvsecrets import' command imports a protected virtualization secret
into the zkey key repository. Like other key import or key generation
commands, additional information can be associated with the imported key,
such as a textual description, the volume to encrypt with together with
the volume type, the sector size, and a dummy passphrase. You can not
associate a set of APQNs, since a protected virtualization secret does
not need or use a crypto card.
This command only works when running in a secure execution guest.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/keystore.c | 112 +++++++++++++++++++++++----------
zkey/keystore.h | 9 ++-
zkey/pvsecrets.c | 126 +++++++++++++++++++++++++++++++++++++
zkey/pvsecrets.h | 7 +++
zkey/zkey.1 | 134 ++++++++++++++++++++++++++++++++++++++++
zkey/zkey.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 513 insertions(+), 32 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 771bc08df..cde0caf58 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -2259,9 +2259,11 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
}
/**
- * Imports a secure key from a file and adds it to the key store
+ * Imports a secure key from a buffer and adds it to the key store
*
* @param[in] keystore the key store
+ * @param[in] secure_key the buffer containing the key
+ * @param[in] secure_key_size the size of the key
* @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
@@ -2274,7 +2276,6 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
* 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 key to import
* @param[in] volume_type the type of volume
* @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
* @param[in] passphrase_file the file name of a file containing a passphrase
@@ -2283,25 +2284,23 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
*
* @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, bool noapqncheck, size_t sector_size,
- const char *import_file, const char *volume_type,
- bool gen_passphrase, const char *passphrase_file,
- struct ext_lib *lib)
+int keystore_import(struct keystore *keystore, unsigned char *secure_key,
+ size_t secure_key_size, const char *name,
+ const char *description, const char *volumes,
+ const char *apqns, bool noapqncheck, size_t sector_size,
+ const char *volume_type, bool gen_passphrase,
+ const char *passphrase_file, struct ext_lib *lib)
{
struct key_filenames file_names = { 0 };
struct properties *key_props = NULL;
- size_t secure_key_size;
const char *key_type;
u8 mkvp[MKVP_LENGTH];
int selected = 1;
- 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");
+ util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0)
@@ -2311,27 +2310,29 @@ int keystore_import_key(struct keystore *keystore, const char *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;
- }
-
key_type = get_key_type(secure_key, secure_key_size);
if (key_type == NULL) {
warnx("Key '%s' is not a valid secure key", name);
- free(secure_key);
rc = -EINVAL;
goto out_free_key_filenames;
}
+ if (!is_secure_key(secure_key, secure_key_size)) {
+ if (apqns != NULL) {
+ warnx("No APQNs can be associated with keys of type %s",
+ key_type);
+ rc = -EINVAL;
+ goto out_free_props;
+ }
+ goto write_key;
+ }
+
rc = get_master_key_verification_pattern(secure_key, secure_key_size,
mkvp, keystore->verbose);
if (rc != 0) {
warnx("Failed to get the master key verification pattern: %s",
strerror(-rc));
- goto out_free_key;
+ goto out_free_props;
}
rc = cross_check_apqns(apqns, mkvp,
@@ -2340,17 +2341,17 @@ int keystore_import_key(struct keystore *keystore, const char *name,
get_card_type_for_keytype(key_type),
true, keystore->verbose);
if (rc == -EINVAL)
- goto out_free_key;
+ goto out_free_props;
if (rc != 0 && rc != -ENOTSUP && noapqncheck == 0) {
warnx("Your master key setup is improper");
- goto out_free_key;
+ goto out_free_props;
}
if (is_cca_aes_cipher_key(secure_key, secure_key_size)) {
if (lib->cca->lib_csulcca == NULL) {
rc = load_cca_library(lib->cca, keystore->verbose);
if (rc != 0)
- goto out_free_key;
+ goto out_free_props;
}
rc = select_cca_adapter_by_mkvp(lib->cca, mkvp, apqns,
@@ -2365,7 +2366,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
warnx("No APQN found that is suitable for "
"working with the secure AES key '%s'", name);
rc = 0;
- goto out_free_key;
+ goto out_free_props;
}
rc = restrict_key_export(lib->cca, secure_key, secure_key_size,
@@ -2375,7 +2376,7 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"key: %s", strerror(-rc));
if (!selected)
print_msg_for_cca_envvars("secure AES key");
- goto out_free_key;
+ goto out_free_props;
}
rc = check_aes_cipher_key(secure_key, secure_key_size);
@@ -2386,15 +2387,14 @@ int keystore_import_key(struct keystore *keystore, const char *name,
if (!prompt_for_yes(keystore->verbose)) {
warnx("Operation aborted");
rc = -ECANCELED;
- goto out_free_key;
+ goto out_free_props;
}
}
}
+write_key:
rc = write_secure_key(file_names.skey_filename, secure_key,
secure_key_size, keystore->verbose);
- free(secure_key);
- secure_key = NULL;
if (rc != 0)
goto out_free_props;
@@ -2414,9 +2414,6 @@ int keystore_import_key(struct keystore *keystore, const char *name,
"Successfully imported a secure key in '%s' and key info in '%s'",
file_names.skey_filename, file_names.info_filename);
-out_free_key:
- if (secure_key != NULL)
- free(secure_key);
out_free_props:
if (key_props != NULL)
properties_free(key_props);
@@ -2431,6 +2428,59 @@ int keystore_import_key(struct keystore *keystore, const char *name,
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] 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 a
+ * 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 key to import
+ * @param[in] volume_type the type of volume
+ * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2
+ * @param[in] passphrase_file the file name of a file containing a passphrase
+ * for LUKS2 (optional, can be NULL)
+ * @param[in] lib the external library struct
+ *
+ * @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, bool noapqncheck, size_t sector_size,
+ const char *import_file, const char *volume_type,
+ bool gen_passphrase, const char *passphrase_file,
+ struct ext_lib *lib)
+{
+ size_t secure_key_size;
+ u8 *secure_key;
+ int rc;
+
+ util_assert(import_file != NULL, "Internal error: import_file is NULL");
+
+ secure_key = read_secure_key(import_file, &secure_key_size,
+ keystore->verbose);
+ if (secure_key == NULL)
+ return -ENOENT;
+
+ rc = keystore_import(keystore, secure_key, secure_key_size, name,
+ description, volumes, apqns, noapqncheck,
+ sector_size, volume_type, gen_passphrase,
+ passphrase_file, lib);
+
+ if (secure_key != NULL)
+ free(secure_key);
+
+ return rc;
+}
/**
* Changes properties of a key in the keystore.
diff --git a/zkey/keystore.h b/zkey/keystore.h
index 1443b5df4..b4cae9aae 100644
--- a/zkey/keystore.h
+++ b/zkey/keystore.h
@@ -3,7 +3,7 @@
*
* Keystore handling functions
*
- * Copyright IBM Corp. 2018, 2020
+ * Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -65,6 +65,13 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
struct kms_option *kms_options,
size_t num_kms_options);
+int keystore_import(struct keystore *keystore, unsigned char *secure_key,
+ size_t secure_key_size, const char *name,
+ const char *description, const char *volumes,
+ const char *apqns, bool noapqncheck, size_t sector_size,
+ const char *volume_type, bool gen_passphrase,
+ const char *passphrase_file, struct ext_lib *lib);
+
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/pvsecrets.c b/zkey/pvsecrets.c
index 7f28febad..a4b3a5a83 100644
--- a/zkey/pvsecrets.c
+++ b/zkey/pvsecrets.c
@@ -552,3 +552,129 @@ int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
return rc;
}
+
+struct build_secret_key_blob_data {
+ unsigned char id[UV_SECRET_ID_LEN];
+ char name[UV_SECRET_ID_LEN];
+ struct pvsecrettoken token;
+ bool found;
+};
+
+/**
+ * Callback used to generate a pvsecrets key blob for a specific secret ID.
+ * Called for each secret.
+ *
+ * @param idx the index of the secret
+ * @param type the type of the secret
+ * @param id the ID of the secret
+ * @param cb_private callback private data
+ *
+ * @returns 0 on success, a negative errno in case of an error
+ */
+static int pvsecrets_build_key_blob_cb(u16 UNUSED(idx), u16 type, u32 len,
+ const u8 id[UV_SECRET_ID_LEN],
+ void *cb_private)
+{
+ struct build_secret_key_blob_data *build_blob_data = cb_private;
+
+ if (build_blob_data->found)
+ return 0;
+
+ if (memcmp(id, build_blob_data->name, UV_SECRET_ID_LEN) != 0 &&
+ memcmp(id, build_blob_data->id, UV_SECRET_ID_LEN) != 0)
+ return 0;
+
+ memset(&build_blob_data->token, 0, sizeof(build_blob_data->token));
+ build_blob_data->token.hdr.type = TOKEN_TYPE_NON_CCA;
+ build_blob_data->token.hdr.version = TOKEN_VERSION_PVSECRET;
+ build_blob_data->token.secret_type = type;
+ build_blob_data->token.secret_len = len;
+ memcpy(build_blob_data->token.secretid, id, UV_SECRET_ID_LEN);
+
+ build_blob_data->found = true;
+
+ return 0;
+}
+
+/**
+ * Imports a protected virtualization secure key from the UV and adds it to the
+ * key store
+ *
+ * @param keystore the key store
+ * @param uv_fd the file descriptor of the ultravisor device
+ * @param secret_id the secret id as 32 byte hex string. Can be NULL if
+ * secret_name is non-NULL.
+ * @param secret_name the secret name. Can be NULL if secret_id is non-NULL.
+ * @param name the name of the key in the repository
+ * @param description textual description of the key (optional, can be NULL)
+ * @param volumes a comma separated list of volumes associated with this
+ * key (optional, can be NULL)
+ * @param volume_type the type of volume
+ * @param sector_size the sector size to use with dm-crypt. It must be a
+ * 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 gen_passphrase if true, generate a (dummy) passphrase for LUKS2
+ * @param passphrase_file the file name of a file containing a passphrase
+ * for LUKS2 (optional, can be NULL)
+ * @param verbose if true, verbose messages are printed
+ *
+ * @returns 0 for success or a negative errno in case of an error
+ */
+int pvsecrets_import(struct keystore *keystore, int uv_fd,
+ const char *secret_id, const char *secret_name,
+ const char *name, const char *description,
+ const char *volumes, const char *volume_type,
+ long sector_size, bool gen_passphrase,
+ const char *passphrase_file, bool verbose)
+{
+ struct build_secret_key_blob_data build_blob_data = { 0 };
+ int rc;
+
+ util_assert(keystore != NULL, "Internal error: keystore is NULL");
+ util_assert(uv_fd != -1, "Internal error: uv_fd is -1");
+ util_assert(secret_id != NULL || secret_name != NULL,
+ "Internal error: secret_id and secrest_name is NULL");
+ util_assert(name != NULL, "Internal error: name is NULL");
+
+ rc = get_secret_id_from_hex_or_name(secret_id, secret_name,
+ build_blob_data.id,
+ build_blob_data.name);
+ if (rc < 0)
+ return rc;
+ if (rc > 0)
+ return -EINVAL;
+
+ rc = uv_list_secrets(uv_fd, pvsecrets_build_key_blob_cb,
+ &build_blob_data, verbose);
+ if (rc != 0) {
+ warnx("Failed to import the pvsecret with %s '%s': %s",
+ secret_id != NULL ? "id" : "name",
+ secret_id != NULL ? secret_id : secret_name,
+ strerror(-rc));
+ return rc;
+ }
+
+ if (!build_blob_data.found) {
+ warnx("The pvsecret with %s '%s' does not exist",
+ secret_id != NULL ? "id" : "name",
+ secret_id != NULL ? secret_id : secret_name);
+ return -ENOENT;
+ }
+
+ if (!is_pvsecret_type_supported(build_blob_data.token.secret_type)) {
+ warnx("The type of the pvsecret with %s '%s' is not supported "
+ "by zkey: %s", secret_id != NULL ? "id" : "name",
+ secret_id != NULL ? secret_id : secret_name,
+ get_pvsecret_type_name(
+ build_blob_data.token.secret_type));
+ return -EINVAL;
+ }
+
+ rc = keystore_import(keystore, (unsigned char *)&build_blob_data.token,
+ sizeof(build_blob_data.token), name, description,
+ volumes, NULL, false, sector_size, volume_type,
+ gen_passphrase, passphrase_file, NULL);
+
+ return rc;
+}
diff --git a/zkey/pvsecrets.h b/zkey/pvsecrets.h
index ad8440350..9503c5155 100644
--- a/zkey/pvsecrets.h
+++ b/zkey/pvsecrets.h
@@ -16,6 +16,7 @@
#include "lib/zt_common.h"
#include "pkey.h"
+#include "keystore.h"
/*
* Definitions for the /dev/uv kernel module interface
@@ -89,5 +90,11 @@ int uv_open_device(bool verbose);
int pvsecrets_list(int uv_fd, bool all, bool hex, const char *type_filter,
const char *secret_id, const char *secret_name,
bool verbose);
+int pvsecrets_import(struct keystore *keystore, int uv_fd,
+ const char *secret_id, const char *secret_name,
+ const char *name, const char *description,
+ const char *volumes, const char *volume_type,
+ long sector_size, bool gen_passphrase,
+ const char *passphrase_file, bool verbose);
#endif
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index 4386629f9..ba71a839b 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -1208,6 +1208,64 @@ are supported by the \fBzkey\fP tool. To list all pvsecret types, specify the
This command is only available when running in a secure execution guest.
Only the \fBroot\fP user is allowed to perform this command.
.
+.SS "Import protected virtualization secrets into the repository"
+.
+.B zkey pvsecrets
+.BR import | im
+.RB [ \-\-pvsecret\-id | \-I
+.IR pvsecret\-id ]
+.RB [ \-\-pvsecret\-name | \-e
+.IR pvsecret\-name ]
+.B \-\-name | \-N
+.IR key\-name
+.RB [ \-\-description | \-d
+.IR description ]
+.RB [ \-\-volumes | \-l
+.IR volume1:dmname1[,volume2:dmname2[,...]] ]
+.RB [ \-\-sector\-size | \-S
+.IR bytes ]
+.RB [ \-\-volume\-type | \-t
+.IR type ]
+.RB [ \-\-gen\-dummy\-passphrase ]
+.RB [ \-\-set\-dummy\-passphrase
+.IR passphrase\-file ]
+.RB [ \-\-verbose | \-V ]
+.
+.PP
+Use the
+.B pvsecrets import
+command to import a protected virtualization (PV) secret into the repository.
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP
+option can be specified. If the pvsecret name is specified, then the
+\fB\-\-name\fP option can be omitted. The name of the protected virtualization
+secret key object in the repository will then be the same as the pvsecret name.
+.PP
+You can use the YAML file that was created when using the \fBpvsecret create\fP
+command for adding the protected virtualization secret:
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP or
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
+You might have to install the \fByq\fP package first.
+.PP
+A protected virtualization secret key object does not contain the key material,
+but only a reference (i.e. the secret ID) to the key in the ultravisor.
+When such a protected virtualization secret key object is used with
+\fBdm\-crypt\fP and the \fBPAES\fP kernel cipher, the key material (i.e. a
+protected key) is retrieved from the ultravisor and the crypto operation is
+performed with it.
+.PP
+When importing a protected virtualization secret in a key repository,
+additional information can be associated with it using the
+.B \-\-description
+,
+.B \-\-volumes
+, or the
+.B \-\-sector\-size
+options. APQNs can not be associated, because protected virtualization secrets
+do not require a crypto card.
+.PP
+This command is only available when running in a secure execution guest.
+Only the \fBroot\fP user is allowed to perform this command.
+.
.
.
.
@@ -2117,6 +2175,82 @@ can be specified, but not both.
.
.
.
+.SS "Options for the pvsecrets import command"
+.TP
+.BR \-I ", " \-\-pvsecret\-id\~\fIpvsecret\-id\fP
+ID of the protected virtualization (PV) secret to import. The pvsecret ID is a
+32 byte hex string, optionally prefixed by \fB0x\fP. You can use the YAML file
+that was created when using the \fBpvsecret create\fP command for adding the
+protected virtualization secret:
+\fB\-\-pvsecret\-id "$(yq .id \fP\fIYAML\-FILE\fP\fB)"\fP.
+You might have to install the \fByq\fP package first.
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
+can be specified, but not both.
+.TP
+.BR \-e ", " \-\-pvsecret\-name\~\fIpvsecret\-name\fP
+Name of the protected virtualization (PV) secret to import. You can use the YAML
+file that was created when using the \fBpvsecret create\fP command for adding
+the protected virtualization secret:
+\fB\-\-pvsecret\-name "$(yq .name \fP\fIYAML\-FILE\fP\fB)"\fP.
+You might have to install the \fByq\fP package first.
+Either the \fB\-\-pvsecret\-id\fP option or the \fB\-\-pvsecret\-name\fP option
+can be specified, but not both. If the \fB\-\-pvsecret\-name\fP option is
+specified, then the \fB\-\-name\fP option can be omitted. The name of the
+protected virtualization secret key object in the repository will then be the
+same as the pvsecret name.
+.TP
+.BR \-N ", " \-\-name\~\fIkey\-name\fP
+Specifies the name of the protected virtualization secret key object in the key
+repository. If the \fB\-\-pvsecret\-name\fP option is specified, then the
+\fB\-\-name\fP option can be omitted. The name of the protected virtualization
+secret in the repository will then be the same as the pvsecret name.
+.TP
+.BR \-d ", " \-\-description\~\fIdescription\fP
+Specifies a textual description for the protected virtualization secret in the
+key repository.
+.TP
+.BR \-l ", " \-\-volumes\~\fIvolume1:dmname1[,volume2:dmname2[,...]]\fP
+Specifies a comma-separated list of volumes (block devices) which are
+associated with the protected virtualization secret in the repository. These
+volumes are to be encrypted using \fBdm\-crypt\fP with the protected
+virtualization secret. The volume association also contains the device-mapper
+name, separated by a colon, used with \fBdm\-crypt\fP. A specific volume can
+only be associated with a single key.
+.TP
+.BR \-S ", " \-\-sector\-size\~\fIbytes\fP
+Specifies the sector size in bytes used with \fBdm\-crypt\fP. It must be a power
+of two and in the range of 512 to 4096 bytes. If omitted, the system default
+sector size is used.
+.TP
+.BR \-t ", " \-\-volume\-type\~\fItype\fP
+Specifies the volume type of the associated volumes used with \fBdm\-crypt\fP.
+Possible values are \fBplain\fP and \fBluks2\fP. If omitted, \fBluks2\fP is
+used. This option is only available if
+.B zkey
+has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled,
+the default volume type is \fBplain\fP.
+.TP
+.BR \-\-gen\-dummy\-passphrase
+Generate a dummy passphrase randomly and associate it with the protected
+virtualization secret used to encrypt LUKS2 volume(s). The LUKS2 passphrase is
+of less or no relevance for the security of the volume(s), when a protected
+virtualization secret is used to encrypt the volume(s), and can therefore be
+stored insecurely inside the key repository. If for a certain usage the
+passphrase is of relevance for security, then do not use this option. This
+option can only be specified for keys with a volume type of \fBluks2\fP.
+.TP
+.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP
+Set a dummy passphrase that is read from the specified file and associate it
+with the protected virtualization secret used to encrypt LUKS2 volume(s).
+The LUKS2 passphrase is of less or no relevance for the security of the
+volume(s), when an protected virtualization secret is used to encrypt the
+volume(s), and can therefore be stored insecurely inside the key repository.
+If for a certain usage the passphrase is of relevance for security, then do
+not use this option. This option can only be specified for keys with a volume
+type of \fBluks2\fP.
+.
+.
+.
.SS "General options"
.TP
.BR \-V ", " \-\-verbose
diff --git a/zkey/zkey.c b/zkey/zkey.c
index adc48d60b..6e9b32af5 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -145,6 +145,7 @@ static struct zkey_globals {
#define COMMAND_KMS_REFRESH "refresh"
#define COMMAND_PVSECRETS "pvsecrets"
#define COMMAND_PVSECRETS_LIST "list"
+#define COMMAND_PVSECRETS_IMPORT "import"
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
@@ -1234,6 +1235,103 @@ static struct util_opt opt_vec[] = {
.command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_LIST,
},
/***********************************************************/
+ {
+ .flags = UTIL_OPT_FLAG_SECTION,
+ .desc = "OPTIONS",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "pvsecret-id", required_argument, NULL, 'I'},
+ .argument = "PVSECRET-ID",
+ .desc = "ID of the protected virtualization (PV) secret to "
+ "import. Either '--pvsecret-id/-I' or "
+ "'--pvsecret-name/-e' can be specified, but not both.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "pvsecret-name", required_argument, NULL, 'e'},
+ .argument = "PVSECRET-NAME",
+ .desc = "Name of the protected virtualization (PV) secret to "
+ "import. Either '--pvsecret-name/-e' or "
+ "'--pvsecret-id/-I' can be specified, but not both. "
+ "If the '--pvsecret-name/-e' option is specified, but "
+ "the '--name/-N' option is omitted, then the imported "
+ "protected virtualisation secret will be named the "
+ "same as the pvsecret name.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "name", required_argument, NULL, 'N'},
+ .argument = "NAME",
+ .desc = "Name of the imported protected virtualisation secret "
+ "in the repository. If the '--name/-N' option is "
+ "omitted, but the '--pvsecret-name/-e' is specified, "
+ "then the imported protected virtualisation secret "
+ "will be named the same as the pvsecret name.",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "description", required_argument, NULL, 'd'},
+ .argument = "DESCRIPTION",
+ .desc = "Textual description of the protected virtualisation "
+ "secret in the repository",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "volumes", required_argument, NULL, 'l'},
+ .argument = "VOLUME:DMNAME[,...]",
+ .desc = "Comma-separated pairs of volume and device-mapper "
+ "names that are associated with the protected "
+ "virtualisation secret in the repository",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "sector-size", required_argument, NULL, 'S'},
+ .argument = "512|4096",
+ .desc = "The sector size used with dm-crypt. It must be a power "
+ "of two and in range 512 - 4096 bytes. If this option "
+ "is omitted, the system default sector size (512) is "
+ "used",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+#ifdef HAVE_LUKS2_SUPPORT
+ {
+ .option = { "volume-type", required_argument, NULL, 't'},
+ .argument = "type",
+ .desc = "The type of the associated volume(s). Possible values "
+ "are 'plain' and 'luks2'. When this option is omitted, "
+ "the default is 'luks2'",
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+#endif
+ {
+ .option = { "gen-dummy-passphrase", 0, NULL,
+ OPT_GEN_DUMMY_PASSPHRASE},
+ .desc = "Generate a dummy passphrase and associate it with the "
+ "protected virtualisation secret used to encrypt LUKS2 "
+ "volume(s). The LUKS2 passphrase is of less or no "
+ "relevance for the security of the volume(s), when an "
+ "protected virtualisation secret is used to encrypt "
+ "the volume(s), and can therefore be stored insecurely "
+ "inside the secure key repository.",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ {
+ .option = { "set-dummy-passphrase", required_argument, NULL,
+ OPT_SET_DUMMY_PASSPHRASE},
+ .argument = "passphrase-file",
+ .desc = "Set a dummy passphrase to be associated with the "
+ "protected virtualisation secret used to encrypt LUKS2 "
+ "volume(s). The LUKS2 passphrase is of less or no "
+ "relevance for the security of the volume(s), when a "
+ "protected virtualisation secret is used to encrypt "
+ "the volume(s), and can therefore be stored insecurely "
+ "inside the secure key repository.",
+ .flags = UTIL_OPT_FLAG_NOSHORT,
+ .command = COMMAND_PVSECRETS " " COMMAND_PVSECRETS_IMPORT,
+ },
+ /***********************************************************/
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
OPT_PLACEHOLDER,
@@ -1339,6 +1437,7 @@ static int command_kms_list(void);
static int command_kms_import(void);
static int command_kms_refresh(void);
static int command_pvsecrets_list(void);
+static int command_pvsecrets_import(void);
static struct zkey_command zkey_kms_commands[] = {
{
@@ -1469,6 +1568,19 @@ static struct zkey_command zkey_pvsecrets_commands[] = {
.has_options = 1,
.need_uv_device = 1,
},
+ {
+ .command = COMMAND_PVSECRETS_IMPORT,
+ .abbrev_len = 2,
+ .function = command_pvsecrets_import,
+ .short_desc = "Imports a protected virtualization (PV) secret",
+ .long_desc = "Imports a protected virtualization (PV) secret "
+ "into the repository. This command is only "
+ "available when running in a secure execution "
+ "guest.",
+ .has_options = 1,
+ .need_keystore = 1,
+ .need_uv_device = 1,
+ },
{ .command = NULL }
};
@@ -3006,6 +3118,51 @@ static int command_pvsecrets_list(void)
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
}
+/*
+ * Command handler for 'pvsecrets import'.
+ *
+ * Import a protected virtualization secret into the repository
+ */
+static int command_pvsecrets_import(void)
+{
+ int rc;
+
+ if (g.secret_id == NULL && g.secret_name == NULL) {
+ misc_print_required_parm("--pvsecret-id/-I or "
+ "--pvsecret-name/-e");
+ return EXIT_FAILURE;
+ }
+ if (g.secret_id != NULL && g.secret_name != NULL) {
+ warnx("Either '--pvsecret-id/-I' or '--pvsecret-name/-e' can "
+ "be specified, but not both");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+ if (g.secret_name == NULL && g.name == NULL) {
+ misc_print_required_parm("--name/-N");
+ return EXIT_FAILURE;
+ }
+
+ if (g.sector_size < 0)
+ g.sector_size = 0;
+
+ if (g.gen_passphrase && g.passphrase_file != NULL) {
+ warnx("Either '--gen-dummy-passphrase' or "
+ "'--set-dummy-passphrase' can be specified, but not "
+ "both");
+ util_prg_print_parse_error();
+ return EXIT_FAILURE;
+ }
+
+ rc = pvsecrets_import(g.keystore, g.uv_fd, g.secret_id, g.secret_name,
+ g.name != NULL ? g.name : g.secret_name,
+ g.description, g.volumes, g.volume_type,
+ g.sector_size, g.gen_passphrase,
+ g.passphrase_file, g.verbose);
+
+ return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
/**
* Opens the keystore. The keystore directory is either the
* default directory or as specified in an environment variable

View File

@ -0,0 +1,137 @@
From 5276d408fd10669b3d8e623455778a675e8dc149 Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Mon, 19 Feb 2024 10:21:06 +0100
Subject: [PATCH] zkey: Reject key generation and APQN association for
PVSECRET-AES keys
Keys of type PVSECRET-AES can not be generated using 'zkey generate'.
Furthermore, APQNs can not be associated with keys of type PVSECRET-AES
via 'zkey change'. Reject that with a proper error message.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/keystore.c | 32 +++++++++++++++++++++++---------
zkey/zkey.1 | 7 +++++++
zkey/zkey.c | 5 +++++
3 files changed, 35 insertions(+), 9 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index cde0caf5..db62e0a6 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -2009,6 +2009,12 @@ int keystore_generate_key(struct keystore *keystore, const char *name,
return -EINVAL;
}
+ if (!is_secure_key_type(key_type)) {
+ warnx("Keys of type %s can not be generated. Use 'zkey "
+ "pvsecret import' instead", key_type);
+ return -EINVAL;
+ }
+
rc = _keystore_get_key_filenames(keystore, name, &file_names);
if (rc != 0)
goto out_free_key_filenames;
@@ -2535,9 +2541,9 @@ int keystore_change_key(struct keystore *keystore, const char *name,
const char *null_ptr = NULL;
char *upd_volumes = NULL;
size_t secure_key_size;
+ u8 *secure_key = NULL;
u8 mkvp[MKVP_LENGTH];
char sect_size[30];
- u8 *secure_key;
bool kms_bound;
int rc;
@@ -2589,13 +2595,6 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
- rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
- apqns, "APQN",
- _keystore_apqn_check,
- &apqn_check);
- if (rc != 0)
- goto out;
-
secure_key = read_secure_key(file_names.skey_filename,
&secure_key_size,
keystore->verbose);
@@ -2604,11 +2603,24 @@ int keystore_change_key(struct keystore *keystore, const char *name,
goto out;
}
+ if (!is_secure_key(secure_key, secure_key_size)) {
+ warnx("No APQNs can be associated with keys of type %s",
+ get_key_type(secure_key, secure_key_size));
+ rc = -EINVAL;
+ goto out;
+ }
+
+ rc = _keystore_change_association(key_props, PROP_NAME_APQNS,
+ apqns, "APQN",
+ _keystore_apqn_check,
+ &apqn_check);
+ if (rc != 0)
+ goto out;
+
rc = get_master_key_verification_pattern(secure_key,
secure_key_size,
mkvp,
keystore->verbose);
- free(secure_key);
if (rc)
goto out;
@@ -2742,6 +2754,8 @@ int keystore_change_key(struct keystore *keystore, const char *name,
free(upd_volumes);
if (upd_volume_type != NULL)
free(upd_volume_type);
+ if (secure_key != NULL)
+ free(secure_key);
if (rc != 0)
pr_verbose(keystore, "Failed to change key '%s': %s",
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index ba71a839..baaf8478 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -402,6 +402,9 @@ additional information can be associated with a secure key using the
.B \-\-sector\-size
options.
.PP
+Keys of type \fBPVSECRET\-AES\fP do not use a cryptographic adapter, thus APQNs
+can not be associated with them.
+.PP
.B Note:
The \fBimport\fP command requires the CCA host library (libcsulcca.so)
to be installed when secure keys of type \fBCCA\-AESCIPHER\fP are imported.
@@ -564,6 +567,10 @@ APQNs that are associated with the key management system plugin.
Other associated information is also changed in the key management system when
changed using the change command.
.PP
+For keys of type \fBPVSECRET\-AES\fP you can not change or set the APQN
+association. These keys do not use a cryptographic adapter, thus APQNs can not
+be associated with them.
+.PP
.B Note:
The secure key itself cannot be changed, only information about the secure
key is changed. To rename a secure key, use the \fBrename\fP command.
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 6e9b32af..36bdbcc0 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -2001,6 +2001,11 @@ static int command_generate(void)
return command_generate_repository();
if (g.key_type == NULL)
g.key_type = KEY_TYPE_CCA_AESDATA;
+ if (!is_secure_key_type(g.key_type)) {
+ warnx("Keys of type '%s' can not be generated. Use 'zkey "
+ "pvsecret import' instead", g.key_type);
+ return -EXIT_FAILURE;
+ }
if (g.pos_arg != NULL) {
if (g.volumes != NULL) {
warnx("Option '--volumes|-l' is not valid for "

View File

@ -0,0 +1,145 @@
From a8eb2bd4e7e74445c953906b33d450c2ace5223f Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Mon, 19 Feb 2024 11:26:41 +0100
Subject: [PATCH] zkey: Reject re-enciphering of PVSECRET-AES keys
Keys of type PVSECRET-AES can not be reenciphered using 'zkey reencipher'
or 'zkey-cryptsetup reencipher'. Reject that with a proper error message.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/keystore.c | 9 +++++++++
zkey/zkey-cryptsetup.1 | 8 ++++++--
zkey/zkey-cryptsetup.c | 16 ++++++++++++----
zkey/zkey.1 | 4 ++++
zkey/zkey.c | 7 +++++++
5 files changed, 38 insertions(+), 6 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index db62e0a6..4f795a28 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -3567,6 +3567,15 @@ static int _keystore_process_reencipher(struct keystore *keystore,
goto out;
}
+ if (!is_secure_key(secure_key, secure_key_size)) {
+ warnx("Key '%s' is of type %s and can not be re-enciphered, "
+ "skipping", name, get_key_type(secure_key,
+ secure_key_size));
+ info->num_skipped++;
+ rc = 0;
+ goto out;
+ }
+
apqns = properties_get(properties, PROP_NAME_APQNS);
if (apqns != NULL)
apqn_list = str_list_split(apqns);
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
index c455f845..185edab9 100644
--- a/zkey/zkey-cryptsetup.1
+++ b/zkey/zkey-cryptsetup.1
@@ -1,8 +1,8 @@
-.\" Copyright IBM Corp. 2018
+.\" Copyright IBM Corp. 2018, 2024
.\" s390-tools is free software; you can redistribute it and/or modify
.\" it under the terms of the MIT license. See LICENSE for details.
.\"
-.TH ZKEY\-CRYPTSETUP 1 "May 2018" "s390-tools"
+.TH ZKEY\-CRYPTSETUP 1 "February 2024" "s390-tools"
.SH NAME
zkey\-cryptsetup \- Manage secure AES volume keys of volumes encrypted with
\fBLUKS2\fP and the \fBpaes\fP cipher
@@ -115,6 +115,10 @@ command to re-encipher a secure AES volume key of a volume encrypted with
re-enciphered when the master key of the cryptographic adapter in CCA or EP11
coprocessor mode changes.
.PP
+Volume keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do
+not use a cryptographic adapter, thus they do not need to be re-enciphered when
+the master key of a cryptographic adapter changes.
+.PP
The cryptographic adapter in CCA coprocessor mode has three different registers
to store master keys:
.RS 2
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
index 8b55f7d1..2b018a2a 100644
--- a/zkey/zkey-cryptsetup.c
+++ b/zkey/zkey-cryptsetup.c
@@ -2,7 +2,7 @@
* zkey-cryptsetup - Re-encipher or validate volume keys of volumes
* encrypted with LUKS2 and the paes cipher.
*
- * Copyright IBM Corp. 2018
+ * Copyright IBM Corp. 2018, 2024
*
* s390-tools is free software; you can redistribute it and/or modify
* it under the terms of the MIT license. See LICENSE for details.
@@ -82,7 +82,7 @@ static const struct util_prg prg = {
{
.owner = "IBM Corp.",
.pub_first = 2018,
- .pub_last = 2018,
+ .pub_last = 2024,
},
UTIL_PRG_COPYRIGHT_END
}
@@ -1609,14 +1609,22 @@ static int reencipher_prepare(int token)
if (rc < 0)
goto out;
+ securekeysize = keysize - integrity_keysize;
+
+ if (!is_secure_key((u8 *)key, securekeysize)) {
+ warnx("The volume key of device '%s' is of type %s and can "
+ "not be re-enciphered", g.pos_arg,
+ get_key_type((u8 *)key, securekeysize));
+ rc = -EINVAL;
+ goto out;
+ }
+
reenc_tok.original_keyslot = rc;
rc = ensure_is_active_keylot(reenc_tok.original_keyslot);
if (rc != 0)
goto out;
- securekeysize = keysize - integrity_keysize;
-
rc = generate_key_verification_pattern((u8 *)key, securekeysize,
reenc_tok.verification_pattern,
sizeof(reenc_tok.verification_pattern),
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index baaf8478..316db5f0 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -266,6 +266,10 @@ command to re-encipher an existing secure key with a new master key.
A secure key must be re-enciphered when the master key of the CCA or EP11
cryptographic adapter changes.
.PP
+Keys of type \fBPVSECRET\-AES\fP can not be re-enciphered. These keys do not
+use a cryptographic adapter, thus they do not need to be re-enciphered when the
+master of a cryptographic adapter changes.
+.PP
The CCA cryptographic adapter has three different registers to store
master keys:
.RS 2
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 36bdbcc0..90b46106 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -2118,6 +2118,13 @@ static int command_reencipher_file(void)
if (secure_key == NULL)
return EXIT_FAILURE;
+ if (!is_secure_key(secure_key, secure_key_size)) {
+ warnx("A key of type %s can not be re-enciphered",
+ get_key_type(secure_key, secure_key_size));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
+
rc = validate_secure_key(g.pkey_fd, secure_key, secure_key_size, NULL,
&is_old_mk, NULL, g.verbose);
if (rc != 0) {

View File

@ -0,0 +1,407 @@
From 833a8e7309ebf0ce70f2ee989ced5f87d6c3550b Mon Sep 17 00:00:00 2001
From: Ingo Franzki <ifranzki@linux.ibm.com>
Date: Mon, 19 Feb 2024 10:25:54 +0100
Subject: [PATCH] zkey: Support validation of key of type PVSECRET-AES
Keys of type PVSECRET-AES can also be verified via the pkey IOCTL
PKEY_VERIFYKEY2, but the card and domain fields must be zero, because such
a key does not use a crypto card. Also XTS keys of type PVSRCRET-AES are
not represented by 2 concatenated keys but by just one key of type
PVSECRET-AES. Thus, special handling is required for XTS keys.
Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com>
Reviewed-by: Jorg Schmidbauer <jschmidb@de.ibm.com>
Signed-off-by: Steffen Eiden <seiden@linux.ibm.com>
---
zkey/keystore.c | 50 ++++++++++++++++++++++--------------
zkey/pkey.c | 46 +++++++++++++++++++++-------------
zkey/zkey-cryptsetup.1 | 4 ++-
zkey/zkey-cryptsetup.c | 45 +++++++++++++++++++--------------
zkey/zkey.1 | 8 +++---
zkey/zkey.c | 57 ++++++++++++++++++++++++------------------
6 files changed, 126 insertions(+), 84 deletions(-)
diff --git a/zkey/keystore.c b/zkey/keystore.c
index 4f795a28..58f27df2 100644
--- a/zkey/keystore.c
+++ b/zkey/keystore.c
@@ -3055,19 +3055,25 @@ static void _keystore_print_record(struct util_rec *rec,
util_rec_set(rec, REC_XTS, is_xts ? "Yes" : "No");
util_rec_set(rec, REC_KEY_TYPE, key_type);
if (validation) {
- if (valid)
- util_rec_set(rec, REC_MASTERKEY,
- "%s master key (MKVP: %s)",
- is_old_mk ? "OLD" : "CURRENT",
- printable_mkvp(
- get_card_type_for_keytype(key_type),
- mkvp));
- else
- util_rec_set(rec, REC_MASTERKEY,
- "(unknown, MKVP: %s)",
- printable_mkvp(
- get_card_type_for_keytype(key_type),
- mkvp));
+ if (mkvp != NULL) {
+ if (valid)
+ util_rec_set(rec, REC_MASTERKEY,
+ "%s master key (MKVP: %s)",
+ is_old_mk ? "OLD" : "CURRENT",
+ printable_mkvp(
+ get_card_type_for_keytype(
+ key_type),
+ mkvp));
+ else
+ util_rec_set(rec, REC_MASTERKEY,
+ "(unknown, MKVP: %s)",
+ printable_mkvp(
+ get_card_type_for_keytype(
+ key_type),
+ mkvp));
+ } else {
+ util_rec_set(rec, REC_MASTERKEY, "(none)");
+ }
}
if (volumes_argz != NULL)
util_rec_set_argz(rec, REC_VOLUMES, volumes_argz,
@@ -3294,17 +3300,22 @@ static int _keystore_process_validate(struct keystore *keystore,
valid = 1;
}
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
- mkvp, keystore->verbose);
- if (rc != 0)
- goto out;
+ if (is_secure_key(secure_key, secure_key_size)) {
+ rc = get_master_key_verification_pattern(secure_key,
+ secure_key_size,
+ mkvp,
+ keystore->verbose);
+ if (rc != 0)
+ goto out;
+ }
_keystore_print_record(info->rec, name, properties, 1,
file_names->skey_filename, secure_key_size,
is_xts_key(secure_key, secure_key_size),
clear_key_bitsize, valid, is_old_mk,
_keystore_reencipher_key_exists(file_names),
- mkvp,
+ is_secure_key(secure_key, secure_key_size) ?
+ mkvp : NULL,
_keystore_passphrase_file_exists(file_names) ?
file_names->pass_filename : NULL);
@@ -3316,7 +3327,8 @@ static int _keystore_process_validate(struct keystore *keystore,
"master key\n", 0);
info->num_warnings++;
}
- if (info->noapqncheck == 0)
+ if (info->noapqncheck == 0 &&
+ is_secure_key(secure_key, secure_key_size))
if (_keystore_display_apqn_status(keystore, properties,
mkvp) != 0)
info->num_warnings++;
diff --git a/zkey/pkey.c b/zkey/pkey.c
index 53c0a550..25deb05a 100644
--- a/zkey/pkey.c
+++ b/zkey/pkey.c
@@ -1287,33 +1287,43 @@ int validate_secure_key(int pkey_fd,
{
struct pkey_verifykey2 verifykey2;
struct pkey_apqn *list = NULL;
+ bool xts, valid, securekey;
u32 i, list_entries = 0;
- bool xts, valid;
- u32 flags;
+ u32 flags = 0;
int rc;
util_assert(pkey_fd != -1, "Internal error: pkey_fd is -1");
util_assert(secure_key != NULL, "Internal error: secure_key is NULL");
- xts = is_xts_key(secure_key, secure_key_size);
+ securekey = is_secure_key(secure_key, secure_key_size);
+ xts = securekey ? is_xts_key(secure_key, secure_key_size) : false;
- flags = PKEY_FLAGS_MATCH_CUR_MKVP;
- if (is_cca_aes_data_key(secure_key, secure_key_size) ||
- is_cca_aes_cipher_key(secure_key, secure_key_size))
- flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
+ if (securekey) {
+ flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+ if (is_cca_aes_data_key(secure_key, secure_key_size) ||
+ is_cca_aes_cipher_key(secure_key, secure_key_size))
+ flags |= PKEY_FLAGS_MATCH_ALT_MKVP;
- rc = build_apqn_list_for_key(pkey_fd, secure_key,
- HALF_KEYSIZE_FOR_XTS(secure_key_size, xts),
- flags, apqns, &list, &list_entries,
- verbose);
- if (rc != 0) {
- pr_verbose(verbose, "Failed to build a list of APQNs that can "
- "validate this secure key: %s", strerror(-rc));
- return rc;
+ rc = build_apqn_list_for_key(pkey_fd, secure_key,
+ HALF_KEYSIZE_FOR_XTS(
+ secure_key_size, xts),
+ flags, apqns, &list, &list_entries,
+ verbose);
+ if (rc != 0) {
+ pr_verbose(verbose, "Failed to build a list of APQNs "
+ "that can validate this secure "
+ "key: %s", strerror(-rc));
+ return rc;
+ }
+ } else {
+ list = util_malloc(sizeof(struct pkey_apqn));
+ list[0].card = 0;
+ list[0].domain = 0;
+ list_entries = 1;
}
if (is_old_mk != NULL)
- *is_old_mk = true;
+ *is_old_mk = securekey ? true : false;
if (clear_key_bitsize != NULL)
*clear_key_bitsize = 0;
@@ -1333,7 +1343,7 @@ int validate_secure_key(int pkey_fd,
continue;
}
- if (is_xts_key(secure_key, secure_key_size)) {
+ if (xts) {
rc = validate_secure_xts_key(pkey_fd, &list[i],
secure_key,
secure_key_size,
@@ -1358,7 +1368,7 @@ int validate_secure_key(int pkey_fd,
* If at least one of the APQNs have a matching current MK,
* then don't report OLD, even if some match the old MK.
*/
- if (is_old_mk &&
+ if (securekey && is_old_mk &&
(verifykey2.flags & PKEY_FLAGS_MATCH_CUR_MKVP))
*is_old_mk = false;
}
diff --git a/zkey/zkey-cryptsetup.1 b/zkey/zkey-cryptsetup.1
index 185edab9..ffd600d4 100644
--- a/zkey/zkey-cryptsetup.1
+++ b/zkey/zkey-cryptsetup.1
@@ -68,7 +68,9 @@ It also displays the attributes of the secure key, such as key size, whether
it is a secure key that can be used for the XTS cipher mode, and the master key
register (CURRENT or OLD) with which the secure key is enciphered.
For further information about master key registers, see the
-\fBreencipher\fP command.
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
+cryptographic adapter, thus no master key information is displayed for such
+keys.
.PP
To open a key slot contained in the LUKS2 header of the volume, a passphrase is
required. You are prompted for the passphrase, unless option
diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c
index 2b018a2a..65716f3b 100644
--- a/zkey/zkey-cryptsetup.c
+++ b/zkey/zkey-cryptsetup.c
@@ -1346,8 +1346,7 @@ static int check_keysize_and_cipher_mode(const u8 *key, size_t keysize)
}
if (strncmp(crypt_get_cipher_mode(g.cd), "xts", 3) == 0) {
- if (keysize < 2 * MIN_SECURE_KEY_SIZE ||
- (key != NULL && !is_xts_key(key, keysize))) {
+ if (key != NULL && !is_xts_key(key, keysize)) {
warnx("The volume key size %lu is not valid for the "
"cipher mode '%s'", keysize,
crypt_get_cipher_mode(g.cd));
@@ -1539,8 +1538,9 @@ static int validate_keyslot(int keyslot, char **key, size_t *keysize,
rc = -EINVAL;
goto out;
}
- pr_verbose("Volume key is currently enciphered with %s master key",
- is_old ? "OLD" : "CURRENT");
+ if (is_secure_key((u8 *)vkey, vkeysize - ikeysize))
+ pr_verbose("Volume key is currently enciphered with %s "
+ "master key", is_old ? "OLD" : "CURRENT");
if (key != NULL)
*key = vkey;
@@ -2023,12 +2023,14 @@ static int command_validate(void)
vp_tok_avail = 1;
}
- rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
- mkvp, g.verbose);
- if (rc != 0) {
- warnx("Failed to get the master key verification pattern: %s",
- strerror(-rc));
- goto out;
+ if (is_secure_key((u8 *)key, seckeysize)) {
+ rc = get_master_key_verification_pattern((u8 *)key, seckeysize,
+ mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification "
+ "pattern: %s", strerror(-rc));
+ goto out;
+ }
}
key_type = get_key_type((u8 *)key, seckeysize);
@@ -2041,15 +2043,19 @@ static int command_validate(void)
printf(" Key type: %s\n", key_type);
if (is_valid) {
printf(" Clear key size: %lu bits\n", clear_keysize);
- printf(" Enciphered with: %s master key (MKVP: "
- "%s)\n", is_old_mk ? "OLD" : "CURRENT",
- printable_mkvp(get_card_type_for_keytype(key_type),
- mkvp));
+ if (is_secure_key((u8 *)key, seckeysize)) {
+ printf(" Enciphered with: %s master key (MKVP: "
+ "%s)\n", is_old_mk ? "OLD" : "CURRENT",
+ printable_mkvp(get_card_type_for_keytype(
+ key_type), mkvp));
+ }
} else {
printf(" Clear key size: (unknown)\n");
- printf(" Enciphered with: (unknown, MKVP: %s)\n",
- printable_mkvp(get_card_type_for_keytype(key_type),
- mkvp));
+ if (is_secure_key((u8 *)key, seckeysize)) {
+ printf(" Enciphered with: (unknown, MKVP: %s)\n",
+ printable_mkvp(get_card_type_for_keytype(
+ key_type), mkvp));
+ }
}
if (vp_tok_avail)
print_verification_pattern(vp_tok.verification_pattern);
@@ -2065,7 +2071,7 @@ static int command_validate(void)
if (!is_valid)
printf("\nATTENTION: The secure volume key is not valid.\n");
- if (is_old_mk)
+ if (is_secure_key((u8 *)key, seckeysize) && is_old_mk)
util_print_indented("\nWARNING: The secure volume key is "
"currently enciphered with the OLD "
"master key. To mitigate the danger of "
@@ -2195,7 +2201,8 @@ static int command_setkey(void)
goto out;
}
- if (is_old_mk) {
+ if (is_secure_key(newkey, newkey_size - integrity_keysize) &&
+ is_old_mk) {
util_asprintf(&msg, "The secure key in file '%s' is "
"enciphered with the master key in the OLD "
"master key register. Do you want to set this "
diff --git a/zkey/zkey.1 b/zkey/zkey.1
index 316db5f0..b44eadf1 100644
--- a/zkey/zkey.1
+++ b/zkey/zkey.1
@@ -209,7 +209,9 @@ It also displays the attributes of the secure key, such as key sizes, whether
it is a secure key that can be used for the XTS cipher mode, the master key
register (CURRENT or OLD) with which the secure key is enciphered, and other key
attributes. For further information about master key registers, see the
-\fBreencipher\fP command.
+\fBreencipher\fP command. Keys of type \fBPVSECRET\-AES\fP do not use a
+cryptographic adapter, thus no master key information is displayed for such
+keys.
.PP
The secure key can either be contained in a file in the file system, or in a
secure key repository. To validate a secure key contained in a file, specify
@@ -1599,8 +1601,8 @@ This option is only used for secure keys contained in the secure key repository.
.TP
.BR \-K ", " \-\-key\-type\~\fItype\fP
Specifies the key type of the secure key. Possible values are
-\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, and \fBEP11\-AES\fP. Only keys with
-the specified key type are listed.
+\fBCCA\-AESDATA\fP, \fBCCA\-AESCIPHER\fP, \fBEP11\-AES\fP, and
+\fBPVSECRET\-AES\fP. Only keys with the specified key type are listed.
This option is only used for secure keys contained in the secure key repository.
.TP
.BR \-L ", " \-\-local\fP
diff --git a/zkey/zkey.c b/zkey/zkey.c
index 90b46106..39a527c4 100644
--- a/zkey/zkey.c
+++ b/zkey/zkey.c
@@ -558,9 +558,10 @@ static struct util_opt opt_vec[] = {
.option = { "key-type", required_argument, NULL, 'K'},
.argument = "type",
.desc = "The type of the key. Possible values are '"
- KEY_TYPE_CCA_AESDATA"', '"KEY_TYPE_CCA_AESCIPHER"' "
- "and '"KEY_TYPE_EP11_AES"'. Use this option to list "
- "all keys with the specified key type.",
+ KEY_TYPE_CCA_AESDATA "', '" KEY_TYPE_CCA_AESCIPHER
+ "', '" KEY_TYPE_EP11_AES "', and '"
+ KEY_TYPE_PVSECRET_AES "'. Use this option to list all "
+ "keys with the specified key type.",
.command = COMMAND_LIST,
},
{
@@ -2345,13 +2346,16 @@ static int command_validate_file(void)
goto out;
}
- rc = get_master_key_verification_pattern(secure_key, secure_key_size,
- mkvp, g.verbose);
- if (rc != 0) {
- warnx("Failed to get the master key verification pattern: %s",
- strerror(-rc));
- rc = EXIT_FAILURE;
- goto out;
+ if (is_secure_key(secure_key, secure_key_size)) {
+ rc = get_master_key_verification_pattern(secure_key,
+ secure_key_size,
+ mkvp, g.verbose);
+ if (rc != 0) {
+ warnx("Failed to get the master key verification "
+ "pattern: %s", strerror(-rc));
+ rc = EXIT_FAILURE;
+ goto out;
+ }
}
key_type = get_key_type(secure_key, secure_key_size);
@@ -2363,25 +2367,30 @@ static int command_validate_file(void)
printf(" Clear key size: %lu bits\n", clear_key_size);
printf(" XTS type key: %s\n",
is_xts_key(secure_key, secure_key_size) ? "Yes" : "No");
- printf(" Enciphered with: %s master key (MKVP: %s)\n",
- is_old_mk ? "OLD" : "CURRENT",
- printable_mkvp(get_card_type_for_keytype(key_type), mkvp));
+ if (is_secure_key(secure_key, secure_key_size)) {
+ printf(" Enciphered with: %s master key (MKVP: %s)\n",
+ is_old_mk ? "OLD" : "CURRENT",
+ printable_mkvp(get_card_type_for_keytype(key_type),
+ mkvp));
+ }
printf(" Verification pattern: %.*s\n", VERIFICATION_PATTERN_LEN / 2,
vp);
printf(" %.*s\n", VERIFICATION_PATTERN_LEN / 2,
&vp[VERIFICATION_PATTERN_LEN / 2]);
- rc = cross_check_apqns(NULL, mkvp,
- get_min_card_level_for_keytype(key_type),
- get_min_fw_version_for_keytype(key_type),
- get_card_type_for_keytype(key_type),
- true, g.verbose);
- if (rc == -EINVAL)
- return EXIT_FAILURE;
- if (rc != 0 && rc != -ENOTSUP) {
- warnx("Your master key setup is improper");
- rc = EXIT_FAILURE;
- goto out;
+ if (is_secure_key(secure_key, secure_key_size)) {
+ rc = cross_check_apqns(NULL, mkvp,
+ get_min_card_level_for_keytype(key_type),
+ get_min_fw_version_for_keytype(key_type),
+ get_card_type_for_keytype(key_type),
+ true, g.verbose);
+ if (rc == -EINVAL)
+ return EXIT_FAILURE;
+ if (rc != 0 && rc != -ENOTSUP) {
+ warnx("Your master key setup is improper");
+ rc = EXIT_FAILURE;
+ goto out;
+ }
}
out:

View File

@ -1,3 +1,16 @@
-------------------------------------------------------------------
Thu Jan 30 08:19:47 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>
- Applied additional patches ( jsc#PED-9561 ( jsc#IBM-1447 ) )
* s390-tools-01-zkey-Add-support-for-retrieving-a-list-of-ultravisor-secrets.patch
* s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
* s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
* s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
* s390-tools-05-zkey-Reject-key-generation-and-APQN-association-for-PVSECRET-AES-keys.patch
* s390-tools-06-zkey-Reject-re-enciphering-of-PVSECRET-AES-keys.patch
* s390-tools-07-zkey-Support-validation-of-key-of-type-PVSECRET-AES.patch
- Revendored vendor.tar.gz
-------------------------------------------------------------------
Thu Jan 9 07:05:53 UTC 2025 - Nikolay Gueorguiev <nikolay.gueorguiev@suse.com>

View File

@ -181,6 +181,14 @@ Patch960: s390-tools-Support-unencrypted-SE-images-01.patch
Patch961: s390-tools-pvimg-info-command-04.patch
Patch962: s390-tools-pvimg-additional-01.patch
###
Patch970: s390-tools-01-zkey-Add-support-for-retrieving-a-list-of-ultravisor-secrets.patch
Patch971: s390-tools-02-zkey-Add-the--pvsecrets-list-command.patch
Patch972: s390-tools-03-zkey-Add-PVSECRETS-AES-key-type.patch
Patch973: s390-tools-04-zkey-Add-the-pvsecrets-import-command.patch
Patch974: s390-tools-05-zkey-Reject-key-generation-and-APQN-association-for-PVSECRET-AES-keys.patch
Patch975: s390-tools-06-zkey-Reject-re-enciphering-of-PVSECRET-AES-keys.patch
Patch976: s390-tools-07-zkey-Support-validation-of-key-of-type-PVSECRET-AES.patch
###
Patch990: s390-tools-slfo-01-parse-ipl-device-for-activation.patch
###

BIN
vendor.tar.gz (Stored with Git LFS)

Binary file not shown.