[PATCH 7/8] keys/trusted_keys: enable PKWM wrapping key selection by label

From: Srish Srinivasan

Date: Thu Aug 27 2026 - 02:25:29 EST


The PKWM trusted source currently uses the default wrapping key for all
seal and unseal operations. However, PKWM supports multiple wrapping keys,
enabling better isolation between trusted keys.

Allow users to provide an active wrapping key of their choice created
through sysfs when sealing trusted keys through a new wrapping_key=<label>
option. If no label is specified, the default wrapping key is used for
sealing.

Signed-off-by: Srish Srinivasan <ssrish@xxxxxxxxxxxxx>
---
.../security/keys/trusted-encrypted.rst | 4 +-
arch/powerpc/include/asm/plpks.h | 10 +++-
arch/powerpc/platforms/pseries/plpks.c | 56 +++++++++----------
include/keys/trusted_pkwm.h | 3 +
security/keys/trusted-keys/trusted_pkwm.c | 36 ++++++++++--
5 files changed, 73 insertions(+), 36 deletions(-)

diff --git a/Documentation/security/keys/trusted-encrypted.rst b/Documentation/security/keys/trusted-encrypted.rst
index ddff7c7c2582..76fda28fddb1 100644
--- a/Documentation/security/keys/trusted-encrypted.rst
+++ b/Documentation/security/keys/trusted-encrypted.rst
@@ -83,7 +83,7 @@ safe.

(5) PKWM (PowerVM Key Wrapping Module: IBM PowerVM + Platform KeyStore)

- Rooted to a unique, per-LPAR key, which is derived from a system-wide,
+ Rooted to unique, per-LPAR keys, which are derived from a system-wide,
randomly generated LPAR root key. Both the per-LPAR keys and the LPAR
root key are stored in hypervisor-owned secure memory at runtime,
and the LPAR root key is additionally persisted in secure locations
@@ -366,6 +366,8 @@ Usage::
0x01: require secure boot to be in either audit or
enforced mode
0x02: require secure boot to be in enforced mode
+ wrapping_key= optional wrapping key label as a string. The default
+ wrapping key is used when no label is provided.

"keyctl print" returns an ASCII hex copy of the sealed key, which is in format
specific to PKWM key-blob implementation. The key length for new keys is
diff --git a/arch/powerpc/include/asm/plpks.h b/arch/powerpc/include/asm/plpks.h
index c39d1f07017e..badb07afe749 100644
--- a/arch/powerpc/include/asm/plpks.h
+++ b/arch/powerpc/include/asm/plpks.h
@@ -50,6 +50,12 @@
#define PLPKS_MAX_TIMEOUT (5 * USEC_PER_SEC)
#define PLPKS_FLUSH_SLEEP 10000 // usec

+// Label for the PKWM default wrapping key
+#define PLPKS_DEFAULT_WRAPKEY_LABEL "default-wrapping-key"
+
+// Component for a PKWM wrapping key
+#define PLPKS_WRAPKEY_COMPONENT "PLPKSWR"
+
struct plpks_var {
char *component;
u8 *name;
@@ -117,10 +123,10 @@ int plpks_config_create_softlink(struct kobject *from);

bool plpks_wrapping_is_supported(void);

-int plpks_gen_wrapping_key(void);
+int plpks_gen_wrapping_key(struct plpks_var *var);

int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
- u8 **output_buf, u64 *output_len);
+ u8 **output_buf, u64 *output_len, struct plpks_var *var);

int plpks_unwrap_object(u8 **input_buf, u64 input_len,
u8 **output_buf, u64 *output_len);
diff --git a/arch/powerpc/platforms/pseries/plpks.c b/arch/powerpc/platforms/pseries/plpks.c
index 48a86497eb2b..f8c263e29bb6 100644
--- a/arch/powerpc/platforms/pseries/plpks.c
+++ b/arch/powerpc/platforms/pseries/plpks.c
@@ -9,9 +9,6 @@

#define pr_fmt(fmt) "plpks: " fmt

-#define PLPKS_WRAPKEY_COMPONENT "PLPKSWR"
-#define PLPKS_DEFAULT_WRAPKEY_LABEL "default-wrapping-key"
-
/*
* To 4K align the {input, output} buffers to the {UN}WRAP H_CALLs
*/
@@ -935,6 +932,7 @@ EXPORT_SYMBOL_GPL(plpks_revoke_is_supported);
/**
* plpks_gen_wrapping_key() - Generate a new random key with the 'wrapping key'
* policy set.
+ * @var: variable representing the wrapping key to be created
*
* The H_PKS_GEN_KEY HCALL makes the hypervisor generate a new random key and
* store the key in a PLPKS object with the provided object label. With the
@@ -960,25 +958,22 @@ EXPORT_SYMBOL_GPL(plpks_revoke_is_supported);
*
* Returns: On success 0 is returned, a negative errno if not.
*/
-int plpks_gen_wrapping_key(void)
+int plpks_gen_wrapping_key(struct plpks_var *var)
{
unsigned long retbuf[PLPAR_HCALL_BUFSIZE] = { 0 };
struct plpks_auth *auth;
struct label *label;
int rc = 0, pseries_status = 0;
- struct plpks_var var = {
- .name = PLPKS_DEFAULT_WRAPKEY_LABEL,
- .namelen = strlen(var.name),
- .policy = PLPKS_WRAPPINGKEY,
- .os = PLPKS_VAR_LINUX,
- .component = PLPKS_WRAPKEY_COMPONENT
- };
+
+ if (!var->name || !*var->name)
+ return -EINVAL;

auth = construct_auth(PLPKS_OS_OWNER);
if (IS_ERR(auth))
return PTR_ERR(auth);

- label = construct_label(var.component, var.os, var.name, var.namelen);
+ label = construct_label(var->component, var->os, var->name,
+ var->namelen);
if (IS_ERR(label)) {
rc = PTR_ERR(label);
goto out;
@@ -986,7 +981,7 @@ int plpks_gen_wrapping_key(void)

rc = plpar_hcall(H_PKS_GEN_KEY, retbuf,
virt_to_phys(auth), virt_to_phys(label),
- label->size, var.policy,
+ label->size, var->policy,
NULL, PLPKS_WRAPPING_KEY_LENGTH);

if (!rc)
@@ -995,11 +990,13 @@ int plpks_gen_wrapping_key(void)
pseries_status = rc;
rc = pseries_status_to_err(rc);

- if (rc && rc != -EEXIST) {
- pr_err("H_PKS_GEN_KEY failed. pseries_status=%d, rc=%d",
- pseries_status, rc);
- } else {
- rc = 0;
+ if (rc) {
+ if (rc == -EEXIST)
+ pr_info("wrapping key <%s> already exists\n",
+ (char *)var->name);
+ else
+ pr_err("H_PKS_GEN_KEY failed. pseries_status=%d, rc=%d\n",
+ pseries_status, rc);
}

kfree(label);
@@ -1010,13 +1007,14 @@ int plpks_gen_wrapping_key(void)
EXPORT_SYMBOL_GPL(plpks_gen_wrapping_key);

/**
- * plpks_wrap_object() - Wrap an object using the default wrapping key stored in
- * the PLPKS.
+ * plpks_wrap_object() - Wrap an object using the specified wrapping key stored
+ * in the PLPKS.
* @input_buf: buffer containing the data to be wrapped
* @input_len: length of the input buffer
* @wrap_flags: object wrapping flags
* @output_buf: buffer to store the wrapped data
* @output_len: length of the output buffer
+ * @var: variable representing the wrapping key to be used
*
* The H_PKS_WRAP_OBJECT HCALL wraps an object using a wrapping key stored in
* the PLPKS and returns the wrapped object to the caller. The caller provides a
@@ -1051,7 +1049,7 @@ EXPORT_SYMBOL_GPL(plpks_gen_wrapping_key);
* Returns: On success 0 is returned, a negative errno if not.
*/
int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
- u8 **output_buf, u64 *output_len)
+ u8 **output_buf, u64 *output_len, struct plpks_var *var)
{
unsigned long retbuf[PLPAR_HCALL9_BUFSIZE] = { 0 };
struct plpks_auth *auth;
@@ -1061,18 +1059,18 @@ int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
int rc = 0, pseries_status = 0;
bool sb_audit_or_enforce_bit = wrap_flags & BIT(0);
bool sb_enforce_bit = wrap_flags & BIT(1);
- struct plpks_var var = {
- .name = PLPKS_DEFAULT_WRAPKEY_LABEL,
- .namelen = strlen(var.name),
- .os = PLPKS_VAR_LINUX,
- .component = PLPKS_WRAPKEY_COMPONENT
- };
+
+ if (!var->name || !*var->name) {
+ pr_err("key label cannot be NULL/empty\n");
+ return -EINVAL;
+ }

auth = construct_auth(PLPKS_OS_OWNER);
if (IS_ERR(auth))
return PTR_ERR(auth);

- label = construct_label(var.component, var.os, var.name, var.namelen);
+ label = construct_label(var->component, var->os, var->name,
+ var->namelen);
if (IS_ERR(label)) {
rc = PTR_ERR(label);
goto out;
@@ -1135,7 +1133,7 @@ int plpks_wrap_object(u8 **input_buf, u64 input_len, u16 wrap_flags,
EXPORT_SYMBOL_GPL(plpks_wrap_object);

/**
- * plpks_unwrap_object() - Unwrap an object using the default wrapping key
+ * plpks_unwrap_object() - Unwrap an object using its associated wrapping key
* stored in the PLPKS.
* @input_buf: buffer containing the data to be unwrapped
* @input_len: length of the input buffer
diff --git a/include/keys/trusted_pkwm.h b/include/keys/trusted_pkwm.h
index 4035b9776394..53a5f780edd2 100644
--- a/include/keys/trusted_pkwm.h
+++ b/include/keys/trusted_pkwm.h
@@ -6,10 +6,13 @@
#include <linux/bitops.h>
#include <linux/printk.h>

+#define WRAPPING_KEY_LABEL_LEN_MAX 239
+
extern struct trusted_key_ops pkwm_trusted_key_ops;

struct trusted_pkwm_options {
u16 wrap_flags;
+ unsigned char wrapping_key_label[WRAPPING_KEY_LABEL_LEN_MAX + 1];
};

static inline void dump_options(struct trusted_key_options *o)
diff --git a/security/keys/trusted-keys/trusted_pkwm.c b/security/keys/trusted-keys/trusted_pkwm.c
index b6b5697426a8..a7a542da0e70 100644
--- a/security/keys/trusted-keys/trusted_pkwm.c
+++ b/security/keys/trusted-keys/trusted_pkwm.c
@@ -13,10 +13,12 @@
enum {
Opt_err,
Opt_wrap_flags,
+ Opt_wrapping_key_label,
};

static const match_table_t key_tokens = {
{Opt_wrap_flags, "wrap_flags=%s"},
+ {Opt_wrapping_key_label, "wrapping_key=%s"},
{Opt_err, NULL}
};

@@ -50,6 +52,12 @@ static int getoptions(char *datablob, struct trusted_key_options *opt)
return -EINVAL;
pkwm->wrap_flags = wrap_flags;
break;
+ case Opt_wrapping_key_label:
+ if (strlen(args[0].from) > WRAPPING_KEY_LABEL_LEN_MAX)
+ return -EINVAL;
+ strscpy(pkwm->wrapping_key_label, args[0].from,
+ sizeof(pkwm->wrapping_key_label));
+ break;
default:
return -EINVAL;
}
@@ -82,6 +90,7 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)
{
struct trusted_key_options *options = NULL;
struct trusted_pkwm_options *pkwm = NULL;
+ struct plpks_var var;
u8 *input_buf, *output_buf;
u64 output_len, input_len;
int rc;
@@ -108,8 +117,18 @@ static int trusted_pkwm_seal(struct trusted_key_payload *p, char *datablob)

pkwm = options->private;

+ if (pkwm->wrapping_key_label[0]) {
+ var.name = (u8 *)pkwm->wrapping_key_label;
+ var.namelen = strlen(pkwm->wrapping_key_label);
+ } else {
+ var.name = (u8 *)PLPKS_DEFAULT_WRAPKEY_LABEL;
+ var.namelen = strlen(PLPKS_DEFAULT_WRAPKEY_LABEL);
+ }
+ var.os = PLPKS_VAR_LINUX;
+ var.component = PLPKS_WRAPKEY_COMPONENT;
+
rc = plpks_wrap_object(&input_buf, input_len, pkwm->wrap_flags,
- &output_buf, &output_len);
+ &output_buf, &output_len, &var);
if (!rc) {
memcpy(p->blob, output_buf, output_len);
p->blob_len = output_len;
@@ -161,16 +180,25 @@ static int trusted_pkwm_unseal(struct trusted_key_payload *p, char *datablob)
static int trusted_pkwm_init(void)
{
int ret;
+ struct plpks_var var;

if (!plpks_wrapping_is_supported()) {
pr_err("H_PKS_WRAP_OBJECT interface not supported\n");
return -ENODEV;
}

- ret = plpks_gen_wrapping_key();
- if (ret) {
+ var = (struct plpks_var) {
+ .name = (u8 *)PLPKS_DEFAULT_WRAPKEY_LABEL,
+ .namelen = strlen(PLPKS_DEFAULT_WRAPKEY_LABEL),
+ .policy = PLPKS_WRAPPINGKEY,
+ .os = PLPKS_VAR_LINUX,
+ .component = PLPKS_WRAPKEY_COMPONENT
+ };
+
+ ret = plpks_gen_wrapping_key(&var);
+ if (ret && ret != -EEXIST) {
pr_err("Failed to generate default wrapping key\n");
- return -EINVAL;
+ return ret;
}

return register_key_type(&key_type_trusted);
--
2.52.0