Blob Blame History Raw
From: Harald Freudenberger <freude@linux.ibm.com>
Date: Wed, 19 Jun 2019 14:26:05 +0200
Subject: s390/pkey: add CCA AES cipher key support
Git-commit: f2bbc96e7cfad3891b7bf9bd3e566b9b7ab4553d
Patch-mainline: v5.4-rc1
References: jsc#SLE-7533 LTC#178844

Introduce new ioctls and structs to be used with these new ioctls
which are able to handle CCA AES secure keys and CCA AES cipher keys:

PKEY_GENSECK2: Generate secure key, version 2.
  Generate either a CCA AES secure key or a CCA AES cipher key.

PKEY_CLR2SECK2: Generate secure key from clear key value, version 2.
  Construct a CCA AES secure key or CCA AES cipher key from a given
  clear key value.

PKEY_VERIFYKEY2: Verify the given secure key, version 2.
  Check for correct key type. If cardnr and domain are given, also
  check if this apqn is able to handle this type of key. If cardnr and
  domain are 0xFFFF, on return these values are filled with an apqn
  able to handle this key. The function also checks for the master key
  verification patterns of the key matching to the current or
  alternate mkvp of the apqn. CCA AES cipher keys are also checked
  for CPACF export allowed (CPRTCPAC flag). Currently CCA AES secure
  keys and CCA AES cipher keys are supported (may get extended in the
  future).

PKEY_KBLOB2PROTK2: Transform a key blob (of any type) into
  a protected key, version 2. Difference to version 1 is only that
  this new ioctl has additional parameters to provide a list of
  apqns to be used for the transformation.

PKEY_APQNS4K: Generate a list of APQNs based on the key blob given.
  Is able to find out which type of secure key is given (CCA AES
  secure key or CCA AES cipher key) and tries to find all matching
  crypto cards based on the MKVP and maybe other criterias (like CCA
  AES cipher keys need a CEX6C or higher). The list of APQNs is
  further filtered by the key's mkvp which needs to match to either
  the current mkvp or the alternate mkvp (which is the old mkvp on CCA
  adapters) of the apqns. The flags argument may be used to limit the
  matching apqns. If the PKEY_FLAGS_MATCH_CUR_MKVP is given, only the
  current mkvp of each apqn is compared. Likewise with the
  PKEY_FLAGS_MATCH_ALT_MKVP. If both are given it is assumed to return
  apqns where either the current or the alternate mkvp matches. If no
  matching APQN is found, the ioctl returns with 0 but the
  apqn_entries value is 0.

PKEY_APQNS4KT: Generate a list of APQNs based on the key type given.
  Build a list of APQNs based on the given key type and maybe further
  restrict the list by given master key verification patterns.
  For different key types there may be different ways to match the
  master key verification patterns. For CCA keys (CCA data key and CCA
  cipher key) the first 8 bytes of cur_mkvp refer to the current mkvp
  value of the apqn and the first 8 bytes of the alt_mkvp refer to the
  old mkvp. The flags argument controls if the apqns current and/or
  alternate mkvp should match. If the PKEY_FLAGS_MATCH_CUR_MKVP is
  given, only the current mkvp of each apqn is compared. Likewise with
  the PKEY_FLAGS_MATCH_ALT_MKVP. If both are given, it is assumed to
  return apqns where either the current or the alternate mkvp
  matches. If no matching APQN is found, the ioctl returns with 0 but
  the apqn_entries value is 0.

These new ioctls are now prepared for another new type of secure key
blob which may come in the future. They all use a pointer to the key
blob and a key blob length information instead of some hardcoded byte
array. They all use the new enums pkey_key_type, pkey_key_size and
pkey_key_info for getting/setting key type, key size and additional
info about the key. All but the PKEY_VERIFY2 ioctl now work based on a
list of apqns. This list is walked through trying to perform the
operation on exactly this apqn without any further checking (like card
type or online state). If the apqn fails, simple the next one in the
list is tried until success (return 0) or the end of the list is
reached (return -1 with errno ENODEV). All apqns in the list need to
be exact apqns (0xFFFF as any card or domain is not allowed). There
are two new ioctls which can be used to build a list of apqns based on
a key or key type and maybe restricted by match to a current or
alternate master key verifcation pattern.

Signed-off-by: Harald Freudenberger <freude@linux.ibm.com>
Reviewed-by: Ingo Franzki <ifranzki@linux.ibm.com>
Signed-off-by: Vasily Gorbik <gor@linux.ibm.com>
Acked-by: Petr Tesarik <ptesarik@suse.com>
---
 arch/s390/include/uapi/asm/pkey.h    |  259 ++++++++++++-
 drivers/s390/crypto/pkey_api.c       |  652 +++++++++++++++++++++++++++++++++--
 drivers/s390/crypto/zcrypt_ccamisc.c |   58 +--
 drivers/s390/crypto/zcrypt_ccamisc.h |    8 
 4 files changed, 900 insertions(+), 77 deletions(-)

--- a/arch/s390/include/uapi/asm/pkey.h
+++ b/arch/s390/include/uapi/asm/pkey.h
@@ -2,7 +2,7 @@
 /*
  * Userspace interface to the pkey device driver
  *
- * Copyright IBM Corp. 2017
+ * Copyright IBM Corp. 2017, 2019
  *
  * Author: Harald Freudenberger <freude@de.ibm.com>
  *
@@ -20,38 +20,74 @@
 
 #define PKEY_IOCTL_MAGIC 'p'
 
-#define SECKEYBLOBSIZE	64     /* secure key blob size is always 64 bytes */
-#define PROTKEYBLOBSIZE 80  /* protected key blob size is always 80 bytes */
-#define MAXPROTKEYSIZE	64  /* a protected key blob may be up to 64 bytes */
-#define MAXCLRKEYSIZE	32     /* a clear key value may be up to 32 bytes */
-
-#define MINKEYBLOBSIZE	SECKEYBLOBSIZE	    /* Minimum size of a key blob */
-#define MAXKEYBLOBSIZE	PROTKEYBLOBSIZE     /* Maximum size of a key blob */
+#define SECKEYBLOBSIZE	64	   /* secure key blob size is always 64 bytes */
+#define PROTKEYBLOBSIZE 80	/* protected key blob size is always 80 bytes */
+#define MAXPROTKEYSIZE	64	/* a protected key blob may be up to 64 bytes */
+#define MAXCLRKEYSIZE	32	   /* a clear key value may be up to 32 bytes */
+#define MAXAESCIPHERKEYSIZE 136  /* our aes cipher keys have always 136 bytes */
+
+/* Minimum and maximum size of a key blob */
+#define MINKEYBLOBSIZE	SECKEYBLOBSIZE
+#define MAXKEYBLOBSIZE	MAXAESCIPHERKEYSIZE
 
 /* defines for the type field within the pkey_protkey struct */
-#define PKEY_KEYTYPE_AES_128  1
-#define PKEY_KEYTYPE_AES_192  2
-#define PKEY_KEYTYPE_AES_256  3
+#define PKEY_KEYTYPE_AES_128		      1
+#define PKEY_KEYTYPE_AES_192		      2
+#define PKEY_KEYTYPE_AES_256		      3
+
+/* the newer ioctls use a pkey_key_type enum for type information */
+enum pkey_key_type {
+	PKEY_TYPE_CCA_DATA   = (__u32) 1,
+	PKEY_TYPE_CCA_CIPHER = (__u32) 2,
+};
+
+/* the newer ioctls use a pkey_key_size enum for key size information */
+enum pkey_key_size {
+	PKEY_SIZE_AES_128 = (__u32) 128,
+	PKEY_SIZE_AES_192 = (__u32) 192,
+	PKEY_SIZE_AES_256 = (__u32) 256,
+	PKEY_SIZE_UNKNOWN = (__u32) 0xFFFFFFFF,
+};
+
+/* some of the newer ioctls use these flags */
+#define PKEY_FLAGS_MATCH_CUR_MKVP  0x00000002
+#define PKEY_FLAGS_MATCH_ALT_MKVP  0x00000004
+
+/* keygenflags defines for CCA AES cipher keys */
+#define PKEY_KEYGEN_XPRT_SYM  0x00008000
+#define PKEY_KEYGEN_XPRT_UASY 0x00004000
+#define PKEY_KEYGEN_XPRT_AASY 0x00002000
+#define PKEY_KEYGEN_XPRT_RAW  0x00001000
+#define PKEY_KEYGEN_XPRT_CPAC 0x00000800
+#define PKEY_KEYGEN_XPRT_DES  0x00000080
+#define PKEY_KEYGEN_XPRT_AES  0x00000040
+#define PKEY_KEYGEN_XPRT_RSA  0x00000008
+
+/* Struct to hold apqn target info (card/domain pair) */
+struct pkey_apqn {
+	__u16 card;
+	__u16 domain;
+};
 
-/* Struct to hold a secure key blob */
+/* Struct to hold a CCA AES secure key blob */
 struct pkey_seckey {
 	__u8  seckey[SECKEYBLOBSIZE];		  /* the secure key blob */
 };
 
 /* Struct to hold protected key and length info */
 struct pkey_protkey {
-	__u32 type;	     /* key type, one of the PKEY_KEYTYPE values */
+	__u32 type;	 /* key type, one of the PKEY_KEYTYPE_AES values */
 	__u32 len;		/* bytes actually stored in protkey[]	 */
 	__u8  protkey[MAXPROTKEYSIZE];	       /* the protected key blob */
 };
 
-/* Struct to hold a clear key value */
+/* Struct to hold an AES clear key value */
 struct pkey_clrkey {
 	__u8  clrkey[MAXCLRKEYSIZE]; /* 16, 24, or 32 byte clear key value */
 };
 
 /*
- * Generate secure key
+ * Generate CCA AES secure key.
  */
 struct pkey_genseck {
 	__u16 cardnr;		    /* in: card to use or FFFF for any	 */
@@ -62,7 +98,7 @@ struct pkey_genseck {
 #define PKEY_GENSECK _IOWR(PKEY_IOCTL_MAGIC, 0x01, struct pkey_genseck)
 
 /*
- * Construct secure key from clear key value
+ * Construct CCA AES secure key from clear key value
  */
 struct pkey_clr2seck {
 	__u16 cardnr;		    /* in: card to use or FFFF for any	 */
@@ -74,7 +110,7 @@ struct pkey_clr2seck {
 #define PKEY_CLR2SECK _IOWR(PKEY_IOCTL_MAGIC, 0x02, struct pkey_clr2seck)
 
 /*
- * Fabricate protected key from a secure key
+ * Fabricate AES protected key from a CCA AES secure key
  */
 struct pkey_sec2protk {
 	__u16 cardnr;		     /* in: card to use or FFFF for any   */
@@ -85,7 +121,7 @@ struct pkey_sec2protk {
 #define PKEY_SEC2PROTK _IOWR(PKEY_IOCTL_MAGIC, 0x03, struct pkey_sec2protk)
 
 /*
- * Fabricate protected key from an clear key value
+ * Fabricate AES protected key from clear key value
  */
 struct pkey_clr2protk {
 	__u32 keytype;		     /* in: key type to generate	  */
@@ -96,7 +132,7 @@ struct pkey_clr2protk {
 
 /*
  * Search for matching crypto card based on the Master Key
- * Verification Pattern provided inside a secure key.
+ * Verification Pattern provided inside a CCA AES secure key.
  */
 struct pkey_findcard {
 	struct pkey_seckey seckey;	       /* in: the secure key blob */
@@ -115,7 +151,7 @@ struct pkey_skey2pkey {
 #define PKEY_SKEY2PKEY _IOWR(PKEY_IOCTL_MAGIC, 0x06, struct pkey_skey2pkey)
 
 /*
- * Verify the given secure key for being able to be useable with
+ * Verify the given CCA AES secure key for being able to be useable with
  * the pkey module. Check for correct key type and check for having at
  * least one crypto card being able to handle this key (master key
  * or old master key verification pattern matches).
@@ -134,7 +170,7 @@ struct pkey_verifykey {
 #define PKEY_VERIFY_ATTR_OLD_MKVP  0x00000100  /* key has old MKVP value */
 
 /*
- * Generate (AES) random protected key.
+ * Generate AES random protected key.
  */
 struct pkey_genprotk {
 	__u32 keytype;			       /* in: key type to generate */
@@ -144,7 +180,7 @@ struct pkey_genprotk {
 #define PKEY_GENPROTK _IOWR(PKEY_IOCTL_MAGIC, 0x08, struct pkey_genprotk)
 
 /*
- * Verify an (AES) protected key.
+ * Verify an AES protected key.
  */
 struct pkey_verifyprotk {
 	struct pkey_protkey protkey;	/* in: the protected key to verify */
@@ -160,7 +196,184 @@ struct pkey_kblob2pkey {
 	__u32 keylen;			/* in: the key blob length */
 	struct pkey_protkey protkey;	/* out: the protected key  */
 };
-
 #define PKEY_KBLOB2PROTK _IOWR(PKEY_IOCTL_MAGIC, 0x0A, struct pkey_kblob2pkey)
 
+/*
+ * Generate secure key, version 2.
+ * Generate either a CCA AES secure key or a CCA AES cipher key.
+ * There needs to be a list of apqns given with at least one entry in there.
+ * All apqns in the list need to be exact apqns, 0xFFFF as ANY card or domain
+ * is not supported. The implementation walks through the list of apqns and
+ * tries to send the request to each apqn without any further checking (like
+ * card type or online state). If the apqn fails, simple the next one in the
+ * list is tried until success (return 0) or the end of the list is reached
+ * (return -1 with errno ENODEV). You may use the PKEY_APQNS4KT ioctl to
+ * generate a list of apqns based on the key type to generate.
+ * The keygenflags argument is passed to the low level generation functions
+ * individual for the key type and has a key type specific meaning. Currently
+ * only CCA AES cipher keys react to this parameter: Use one or more of the
+ * PKEY_KEYGEN_* flags to widen the export possibilities. By default a cipher
+ * key is only exportable for CPACF (PKEY_KEYGEN_XPRT_CPAC).
+ */
+struct pkey_genseck2 {
+	struct pkey_apqn __user *apqns; /* in: ptr to list of apqn targets*/
+	__u32 apqn_entries;	    /* in: # of apqn target list entries  */
+	enum pkey_key_type type;    /* in: key type to generate		  */
+	enum pkey_key_size size;    /* in: key size to generate		  */
+	__u32 keygenflags;	    /* in: key generation flags		  */
+	__u8 __user *key;	    /* in: pointer to key blob buffer	  */
+	__u32 keylen;		    /* in: available key blob buffer size */
+				    /* out: actual key blob size	  */
+};
+#define PKEY_GENSECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x11, struct pkey_genseck2)
+
+/*
+ * Generate secure key from clear key value, version 2.
+ * Construct a CCA AES secure key or CCA AES cipher key from a given clear key
+ * value.
+ * There needs to be a list of apqns given with at least one entry in there.
+ * All apqns in the list need to be exact apqns, 0xFFFF as ANY card or domain
+ * is not supported. The implementation walks through the list of apqns and
+ * tries to send the request to each apqn without any further checking (like
+ * card type or online state). If the apqn fails, simple the next one in the
+ * list is tried until success (return 0) or the end of the list is reached
+ * (return -1 with errno ENODEV). You may use the PKEY_APQNS4KT ioctl to
+ * generate a list of apqns based on the key type to generate.
+ * The keygenflags argument is passed to the low level generation functions
+ * individual for the key type and has a key type specific meaning. Currently
+ * only CCA AES cipher keys react to this parameter: Use one or more of the
+ * PKEY_KEYGEN_* flags to widen the export possibilities. By default a cipher
+ * key is only exportable for CPACF (PKEY_KEYGEN_XPRT_CPAC).
+ */
+struct pkey_clr2seck2 {
+	struct pkey_apqn __user *apqns; /* in: ptr to list of apqn targets */
+	__u32 apqn_entries;	    /* in: # of apqn target list entries   */
+	enum pkey_key_type type;    /* in: key type to generate		   */
+	enum pkey_key_size size;    /* in: key size to generate		   */
+	__u32 keygenflags;	    /* in: key generation flags		   */
+	struct pkey_clrkey clrkey;  /* in: the clear key value		   */
+	__u8 __user *key;	    /* in: pointer to key blob buffer	   */
+	__u32 keylen;		    /* in: available key blob buffer size  */
+				    /* out: actual key blob size	   */
+};
+#define PKEY_CLR2SECK2 _IOWR(PKEY_IOCTL_MAGIC, 0x12, struct pkey_clr2seck2)
+
+/*
+ * Verify the given secure key, version 2.
+ * Check for correct key type. If cardnr and domain are given (are not
+ * 0xFFFF) also check if this apqn is able to handle this type of key.
+ * If cardnr and/or domain is 0xFFFF, on return these values are filled
+ * with one apqn able to handle this key.
+ * The function also checks for the master key verification patterns
+ * of the key matching to the current or alternate mkvp of the apqn.
+ * Currently CCA AES secure keys and CCA AES cipher keys are supported.
+ * The flags field is updated with some additional info about the apqn mkvp
+ * match: If the current mkvp matches to the key's mkvp then the
+ * PKEY_FLAGS_MATCH_CUR_MKVP bit is set, if the alternate mkvp matches to
+ * the key's mkvp the PKEY_FLAGS_MATCH_ALT_MKVP is set. For CCA keys the
+ * alternate mkvp is the old master key verification pattern.
+ * CCA AES secure keys are also checked to have the CPACF export allowed
+ * bit enabled (XPRTCPAC) in the kmf1 field.
+ * The ioctl returns 0 as long as the given or found apqn matches to
+ * matches with the current or alternate mkvp to the key's mkvp. If the given
+ * apqn does not match or there is no such apqn found, -1 with errno
+ * ENODEV is returned.
+ */
+struct pkey_verifykey2 {
+	__u8 __user *key;	    /* in: pointer to key blob		 */
+	__u32 keylen;		    /* in: key blob size		 */
+	__u16 cardnr;		    /* in/out: card number		 */
+	__u16 domain;		    /* in/out: domain number		 */
+	enum pkey_key_type type;    /* out: the key type		 */
+	enum pkey_key_size size;    /* out: the key size		 */
+	__u32 flags;		    /* out: additional key info flags	 */
+};
+#define PKEY_VERIFYKEY2 _IOWR(PKEY_IOCTL_MAGIC, 0x17, struct pkey_verifykey2)
+
+/*
+ * Transform a key blob (of any type) into a protected key, version 2.
+ * There needs to be a list of apqns given with at least one entry in there.
+ * All apqns in the list need to be exact apqns, 0xFFFF as ANY card or domain
+ * is not supported. The implementation walks through the list of apqns and
+ * tries to send the request to each apqn without any further checking (like
+ * card type or online state). If the apqn fails, simple the next one in the
+ * list is tried until success (return 0) or the end of the list is reached
+ * (return -1 with errno ENODEV). You may use the PKEY_APQNS4K ioctl to
+ * generate a list of apqns based on the key.
+ */
+struct pkey_kblob2pkey2 {
+	__u8 __user *key;	     /* in: pointer to key blob		   */
+	__u32 keylen;		     /* in: key blob size		   */
+	struct pkey_apqn __user *apqns; /* in: ptr to list of apqn targets */
+	__u32 apqn_entries;	     /* in: # of apqn target list entries  */
+	struct pkey_protkey protkey; /* out: the protected key		   */
+};
+#define PKEY_KBLOB2PROTK2 _IOWR(PKEY_IOCTL_MAGIC, 0x1A, struct pkey_kblob2pkey2)
+
+/*
+ * Build a list of APQNs based on a key blob given.
+ * Is able to find out which type of secure key is given (CCA AES secure
+ * key or CCA AES cipher key) and tries to find all matching crypto cards
+ * based on the MKVP and maybe other criterias (like CCA AES cipher keys
+ * need a CEX5C or higher). The list of APQNs is further filtered by the key's
+ * mkvp which needs to match to either the current mkvp or the alternate mkvp
+ * (which is the old mkvp on CCA adapters) of the apqns. The flags argument may
+ * be used to limit the matching apqns. If the PKEY_FLAGS_MATCH_CUR_MKVP is
+ * given, only the current mkvp of each apqn is compared. Likewise with the
+ * PKEY_FLAGS_MATCH_ALT_MKVP. If both are given, it is assumed to
+ * return apqns where either the current or the alternate mkvp
+ * matches. At least one of the matching flags needs to be given.
+ * The list of matching apqns is stored into the space given by the apqns
+ * argument and the number of stored entries goes into apqn_entries. If the list
+ * is empty (apqn_entries is 0) the apqn_entries field is updated to the number
+ * of apqn targets found and the ioctl returns with 0. If apqn_entries is > 0
+ * but the number of apqn targets does not fit into the list, the apqn_targets
+ * field is updatedd with the number of reqired entries but there are no apqn
+ * values stored in the list and the ioctl returns with ENOSPC. If no matching
+ * APQN is found, the ioctl returns with 0 but the apqn_entries value is 0.
+ */
+struct pkey_apqns4key {
+	__u8 __user *key;	   /* in: pointer to key blob		      */
+	__u32 keylen;		   /* in: key blob size			      */
+	__u32 flags;		   /* in: match controlling flags	      */
+	struct pkey_apqn __user *apqns; /* in/out: ptr to list of apqn targets*/
+	__u32 apqn_entries;	   /* in: max # of apqn entries in the list   */
+				   /* out: # apqns stored into the list	      */
+};
+#define PKEY_APQNS4K _IOWR(PKEY_IOCTL_MAGIC, 0x1B, struct pkey_apqns4key)
+
+/*
+ * Build a list of APQNs based on a key type given.
+ * Build a list of APQNs based on a given key type and maybe further
+ * restrict the list by given master key verification patterns.
+ * For different key types there may be different ways to match the
+ * master key verification patterns. For CCA keys (CCA data key and CCA
+ * cipher key) the first 8 bytes of cur_mkvp refer to the current mkvp value
+ * of the apqn and the first 8 bytes of the alt_mkvp refer to the old mkvp.
+ * The flags argument controls if the apqns current and/or alternate mkvp
+ * should match. If the PKEY_FLAGS_MATCH_CUR_MKVP is given, only the current
+ * mkvp of each apqn is compared. Likewise with the PKEY_FLAGS_MATCH_ALT_MKVP.
+ * If both are given, it is assumed to return apqns where either the
+ * current or the alternate mkvp matches. If no match flag is given
+ * (flags is 0) the mkvp values are ignored for the match process.
+ * The list of matching apqns is stored into the space given by the apqns
+ * argument and the number of stored entries goes into apqn_entries. If the list
+ * is empty (apqn_entries is 0) the apqn_entries field is updated to the number
+ * of apqn targets found and the ioctl returns with 0. If apqn_entries is > 0
+ * but the number of apqn targets does not fit into the list, the apqn_targets
+ * field is updatedd with the number of reqired entries but there are no apqn
+ * values stored in the list and the ioctl returns with ENOSPC. If no matching
+ * APQN is found, the ioctl returns with 0 but the apqn_entries value is 0.
+ */
+struct pkey_apqns4keytype {
+	enum pkey_key_type type;   /* in: key type			      */
+	__u8  cur_mkvp[32];	   /* in: current mkvp			      */
+	__u8  alt_mkvp[32];	   /* in: alternate mkvp		      */
+	__u32 flags;		   /* in: match controlling flags	      */
+	struct pkey_apqn __user *apqns; /* in/out: ptr to list of apqn targets*/
+	__u32 apqn_entries;	   /* in: max # of apqn entries in the list   */
+				   /* out: # apqns stored into the list	      */
+};
+#define PKEY_APQNS4KT _IOWR(PKEY_IOCTL_MAGIC, 0x1C, struct pkey_apqns4keytype)
+
 #endif /* _UAPI_PKEY_H */
--- a/drivers/s390/crypto/pkey_api.c
+++ b/drivers/s390/crypto/pkey_api.c
@@ -30,6 +30,9 @@ MODULE_LICENSE("GPL");
 MODULE_AUTHOR("IBM Corporation");
 MODULE_DESCRIPTION("s390 protected key interface");
 
+#define KEYBLOBBUFSIZE 8192  /* key buffer size used for internal processing */
+#define MAXAPQNSINLIST 64    /* max 64 apqns within a apqn list */
+
 /* mask of available pckmo subfunctions, fetched once at module init */
 static cpacf_mask_t pckmo_functions;
 
@@ -126,27 +129,39 @@ static int pkey_clr2protkey(u32 keytype,
 /*
  * Find card and transform secure key into protected key.
  */
-static int pkey_skey2pkey(const struct pkey_seckey *seckey,
-			  struct pkey_protkey *pkey)
+static int pkey_skey2pkey(const u8 *key, struct pkey_protkey *pkey)
 {
-	u16 cardnr, domain;
 	int rc, verify;
+	u16 cardnr, domain;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
 
 	/*
-	 * The cca_sec2protkey call may fail when a card has been
+	 * The cca_xxx2protkey call may fail when a card has been
 	 * addressed where the master key was changed after last fetch
 	 * of the mkvp into the cache. Try 3 times: First witout verify
 	 * then with verify and last round with verify and old master
 	 * key verification pattern match not ignored.
 	 */
 	for (verify = 0; verify < 3; verify++) {
-		rc = cca_findcard(seckey->seckey, &cardnr, &domain, verify);
+		rc = cca_findcard(key, &cardnr, &domain, verify);
 		if (rc < 0)
 			continue;
 		if (rc > 0 && verify < 2)
 			continue;
-		rc = cca_sec2protkey(cardnr, domain, seckey->seckey,
-				     pkey->protkey, &pkey->len, &pkey->type);
+		switch (hdr->version) {
+		case TOKVER_CCA_AES:
+			rc = cca_sec2protkey(cardnr, domain,
+					     key, pkey->protkey,
+					     &pkey->len, &pkey->type);
+			break;
+		case TOKVER_CCA_VLSC:
+			rc = cca_cipher2protkey(cardnr, domain,
+						key, pkey->protkey,
+						&pkey->len, &pkey->type);
+			break;
+		default:
+			return -EINVAL;
+		}
 		if (rc == 0)
 			break;
 	}
@@ -324,14 +339,18 @@ static int pkey_ccainttok2pkey(const u8
 	case TOKVER_CCA_AES:
 		if (keylen != sizeof(struct secaeskeytoken))
 			return -EINVAL;
-
-		return pkey_skey2pkey((struct pkey_seckey *)key,
-				      protkey);
+		break;
+	case TOKVER_CCA_VLSC:
+		if (keylen < hdr->len || keylen > MAXCCAVLSCTOKENSIZE)
+			return -EINVAL;
+		break;
 	default:
 		DEBUG_ERR("%s unknown/unsupported CCA internal token version %d\n",
 			  __func__, hdr->version);
 		return -EINVAL;
 	}
+
+	return pkey_skey2pkey(key, protkey);
 }
 
 /*
@@ -340,28 +359,394 @@ static int pkey_ccainttok2pkey(const u8
 int pkey_keyblob2pkey(const u8 *key, u32 keylen,
 		      struct pkey_protkey *protkey)
 {
+	int rc;
 	struct keytoken_header *hdr = (struct keytoken_header *)key;
 
-	if (keylen < sizeof(struct keytoken_header))
+	if (keylen < sizeof(struct keytoken_header)) {
+		DEBUG_ERR("%s invalid keylen %d\n", __func__, keylen);
 		return -EINVAL;
+	}
 
 	switch (hdr->type) {
 	case TOKTYPE_NON_CCA:
-		return pkey_nonccatok2pkey(key, keylen, protkey);
+		rc = pkey_nonccatok2pkey(key, keylen, protkey);
+		break;
 	case TOKTYPE_CCA_INTERNAL:
-		return pkey_ccainttok2pkey(key, keylen, protkey);
+		rc = pkey_ccainttok2pkey(key, keylen, protkey);
+		break;
 	default:
-		DEBUG_ERR("%s unknown/unsupported blob type %d\n", __func__,
-			  hdr->type);
+		DEBUG_ERR("%s unknown/unsupported blob type %d\n",
+			  __func__, hdr->type);
 		return -EINVAL;
 	}
+
+	DEBUG_DBG("%s rc=%d\n", __func__, rc);
+	return rc;
+
 }
 EXPORT_SYMBOL(pkey_keyblob2pkey);
 
+static int pkey_genseckey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			   enum pkey_key_type ktype, enum pkey_key_size ksize,
+			   u32 kflags, u8 *keybuf, size_t *keybufsize)
+{
+	int i, card, dom, rc;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	/* check key type and size */
+	switch (ktype) {
+	case PKEY_TYPE_CCA_DATA:
+	case PKEY_TYPE_CCA_CIPHER:
+		if (*keybufsize < SECKEYBLOBSIZE)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (ksize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_SIZE_AES_192:
+	case PKEY_SIZE_AES_256:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (ktype == PKEY_TYPE_CCA_DATA) {
+			rc = cca_genseckey(card, dom, ksize, keybuf);
+			*keybufsize = (rc ? 0 : SECKEYBLOBSIZE);
+		} else /* TOKVER_CCA_VLSC */
+			rc = cca_gencipherkey(card, dom, ksize, kflags,
+					      keybuf, keybufsize);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_clr2seckey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			    enum pkey_key_type ktype, enum pkey_key_size ksize,
+			    u32 kflags, const u8 *clrkey,
+			    u8 *keybuf, size_t *keybufsize)
+{
+	int i, card, dom, rc;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	/* check key type and size */
+	switch (ktype) {
+	case PKEY_TYPE_CCA_DATA:
+	case PKEY_TYPE_CCA_CIPHER:
+		if (*keybufsize < SECKEYBLOBSIZE)
+			return -EINVAL;
+		break;
+	default:
+		return -EINVAL;
+	}
+	switch (ksize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_SIZE_AES_192:
+	case PKEY_SIZE_AES_256:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (ktype == PKEY_TYPE_CCA_DATA) {
+			rc = cca_clr2seckey(card, dom, ksize,
+					    clrkey, keybuf);
+			*keybufsize = (rc ? 0 : SECKEYBLOBSIZE);
+		} else /* TOKVER_CCA_VLSC */
+			rc = cca_clr2cipherkey(card, dom, ksize, kflags,
+					       clrkey, keybuf, keybufsize);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_verifykey2(const u8 *key, size_t keylen,
+			   u16 *cardnr, u16 *domain,
+			   enum pkey_key_type *ktype,
+			   enum pkey_key_size *ksize, u32 *flags)
+{
+	int rc;
+	u32 _nr_apqns, *_apqns = NULL;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	if (keylen < sizeof(struct keytoken_header) ||
+	    hdr->type != TOKTYPE_CCA_INTERNAL)
+		return -EINVAL;
+
+	if (hdr->version == TOKVER_CCA_AES) {
+		struct secaeskeytoken *t = (struct secaeskeytoken *)key;
+
+		rc = cca_check_secaeskeytoken(debug_info, 3, key, 0);
+		if (rc)
+			goto out;
+		if (ktype)
+			*ktype = PKEY_TYPE_CCA_DATA;
+		if (ksize)
+			*ksize = (enum pkey_key_size) t->bitsize;
+
+		rc = cca_findcard2(&_apqns, &_nr_apqns, *cardnr, *domain,
+				   ZCRYPT_CEX3C, t->mkvp, 0, 1);
+		if (rc == 0 && flags)
+			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+		if (rc == -ENODEV) {
+			rc = cca_findcard2(&_apqns, &_nr_apqns,
+					   *cardnr, *domain,
+					   ZCRYPT_CEX3C, 0, t->mkvp, 1);
+			if (rc == 0 && flags)
+				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
+		}
+		if (rc)
+			goto out;
+
+		*cardnr = ((struct pkey_apqn *)_apqns)->card;
+		*domain = ((struct pkey_apqn *)_apqns)->domain;
+
+	} else if (hdr->version == TOKVER_CCA_VLSC) {
+		struct cipherkeytoken *t = (struct cipherkeytoken *)key;
+
+		rc = cca_check_secaescipherkey(debug_info, 3, key, 0, 1);
+		if (rc)
+			goto out;
+		if (ktype)
+			*ktype = PKEY_TYPE_CCA_CIPHER;
+		if (ksize) {
+			*ksize = PKEY_SIZE_UNKNOWN;
+			if (!t->plfver && t->wpllen == 512)
+				*ksize = PKEY_SIZE_AES_128;
+			else if (!t->plfver && t->wpllen == 576)
+				*ksize = PKEY_SIZE_AES_192;
+			else if (!t->plfver && t->wpllen == 640)
+				*ksize = PKEY_SIZE_AES_256;
+		}
+
+		rc = cca_findcard2(&_apqns, &_nr_apqns, *cardnr, *domain,
+				   ZCRYPT_CEX6, t->mkvp0, 0, 1);
+		if (rc == 0 && flags)
+			*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
+		if (rc == -ENODEV) {
+			rc = cca_findcard2(&_apqns, &_nr_apqns,
+					   *cardnr, *domain,
+					   ZCRYPT_CEX6, 0, t->mkvp0, 1);
+			if (rc == 0 && flags)
+				*flags = PKEY_FLAGS_MATCH_ALT_MKVP;
+		}
+		if (rc)
+			goto out;
+
+		*cardnr = ((struct pkey_apqn *)_apqns)->card;
+		*domain = ((struct pkey_apqn *)_apqns)->domain;
+
+	} else
+		rc = -EINVAL;
+
+out:
+	kfree(_apqns);
+	return rc;
+}
+
+static int pkey_keyblob2pkey2(const struct pkey_apqn *apqns, size_t nr_apqns,
+			      const u8 *key, size_t keylen,
+			      struct pkey_protkey *pkey)
+{
+	int i, card, dom, rc;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	/* check for at least one apqn given */
+	if (!apqns || !nr_apqns)
+		return -EINVAL;
+
+	if (keylen < sizeof(struct keytoken_header))
+		return -EINVAL;
+
+	switch (hdr->type) {
+	case TOKTYPE_NON_CCA:
+		return pkey_nonccatok2pkey(key, keylen, pkey);
+	case TOKTYPE_CCA_INTERNAL:
+		switch (hdr->version) {
+		case TOKVER_CCA_AES:
+			if (keylen != sizeof(struct secaeskeytoken))
+				return -EINVAL;
+			if (cca_check_secaeskeytoken(debug_info, 3, key, 0))
+				return -EINVAL;
+			break;
+		case TOKVER_CCA_VLSC:
+			if (keylen < hdr->len || keylen > MAXCCAVLSCTOKENSIZE)
+				return -EINVAL;
+			if (cca_check_secaescipherkey(debug_info, 3, key, 0, 1))
+				return -EINVAL;
+			break;
+		default:
+			DEBUG_ERR("%s unknown CCA internal token version %d\n",
+				  __func__, hdr->version);
+			return -EINVAL;
+		}
+		break;
+	default:
+		DEBUG_ERR("%s unknown/unsupported blob type %d\n",
+			  __func__, hdr->type);
+		return -EINVAL;
+	}
+
+	/* simple try all apqns from the list */
+	for (i = 0, rc = -ENODEV; i < nr_apqns; i++) {
+		card = apqns[i].card;
+		dom = apqns[i].domain;
+		if (hdr->version == TOKVER_CCA_AES)
+			rc = cca_sec2protkey(card, dom, key, pkey->protkey,
+					     &pkey->len, &pkey->type);
+		else /* TOKVER_CCA_VLSC */
+			rc = cca_cipher2protkey(card, dom, key, pkey->protkey,
+						&pkey->len, &pkey->type);
+		if (rc == 0)
+			break;
+	}
+
+	return rc;
+}
+
+static int pkey_apqns4key(const u8 *key, size_t keylen, u32 flags,
+			  struct pkey_apqn *apqns, size_t *nr_apqns)
+{
+	int rc = EINVAL;
+	u32 _nr_apqns, *_apqns = NULL;
+	struct keytoken_header *hdr = (struct keytoken_header *)key;
+
+	if (keylen < sizeof(struct keytoken_header) ||
+	    hdr->type != TOKTYPE_CCA_INTERNAL ||
+	    flags == 0)
+		return -EINVAL;
+
+	if (hdr->version == TOKVER_CCA_AES || hdr->version == TOKVER_CCA_VLSC) {
+		int minhwtype = ZCRYPT_CEX3C;
+		u64 cur_mkvp = 0, old_mkvp = 0;
+
+		if (hdr->version == TOKVER_CCA_AES) {
+			struct secaeskeytoken *t = (struct secaeskeytoken *)key;
+
+			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+				cur_mkvp = t->mkvp;
+			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+				old_mkvp = t->mkvp;
+		} else {
+			struct cipherkeytoken *t = (struct cipherkeytoken *)key;
+
+			minhwtype = ZCRYPT_CEX6;
+			if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+				cur_mkvp = t->mkvp0;
+			if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+				old_mkvp = t->mkvp0;
+		}
+		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+				   minhwtype, cur_mkvp, old_mkvp, 1);
+		if (rc)
+			goto out;
+		if (apqns) {
+			if (*nr_apqns < _nr_apqns)
+				rc = -ENOSPC;
+			else
+				memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
+		}
+		*nr_apqns = _nr_apqns;
+	}
+
+out:
+	kfree(_apqns);
+	return rc;
+}
+
+static int pkey_apqns4keytype(enum pkey_key_type ktype,
+			      u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
+			      struct pkey_apqn *apqns, size_t *nr_apqns)
+{
+	int rc = -EINVAL;
+	u32 _nr_apqns, *_apqns = NULL;
+
+	if (ktype == PKEY_TYPE_CCA_DATA || ktype == PKEY_TYPE_CCA_CIPHER) {
+		u64 cur_mkvp = 0, old_mkvp = 0;
+		int minhwtype = ZCRYPT_CEX3C;
+
+		if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
+			cur_mkvp = *((u64 *) cur_mkvp);
+		if (flags & PKEY_FLAGS_MATCH_ALT_MKVP)
+			old_mkvp = *((u64 *) alt_mkvp);
+		if (ktype == PKEY_TYPE_CCA_CIPHER)
+			minhwtype = ZCRYPT_CEX6;
+		rc = cca_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
+				   minhwtype, cur_mkvp, old_mkvp, 1);
+		if (rc)
+			goto out;
+		if (apqns) {
+			if (*nr_apqns < _nr_apqns)
+				rc = -ENOSPC;
+			else
+				memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
+		}
+		*nr_apqns = _nr_apqns;
+	}
+
+out:
+	kfree(_apqns);
+	return rc;
+}
+
 /*
  * File io functions
  */
 
+static void *_copy_key_from_user(void __user *ukey, size_t keylen)
+{
+	void *kkey;
+
+	if (!ukey || keylen < MINKEYBLOBSIZE || keylen > KEYBLOBBUFSIZE)
+		return ERR_PTR(-EINVAL);
+	kkey = kmalloc(keylen, GFP_KERNEL);
+	if (!kkey)
+		return ERR_PTR(-ENOMEM);
+	if (copy_from_user(kkey, ukey, keylen)) {
+		kfree(kkey);
+		return ERR_PTR(-EFAULT);
+	}
+
+	return kkey;
+}
+
+static void *_copy_apqns_from_user(void __user *uapqns, size_t nr_apqns)
+{
+	void *kapqns = NULL;
+	size_t nbytes;
+
+	if (uapqns && nr_apqns > 0) {
+		nbytes = nr_apqns * sizeof(struct pkey_apqn);
+		kapqns = kmalloc(nbytes, GFP_KERNEL);
+		if (!kapqns)
+			return ERR_PTR(-ENOMEM);
+		if (copy_from_user(kapqns, uapqns, nbytes))
+			return ERR_PTR(-EFAULT);
+	}
+
+	return kapqns;
+}
+
 static long pkey_unlocked_ioctl(struct file *filp, unsigned int cmd,
 				unsigned long arg)
 {
@@ -452,7 +837,7 @@ static long pkey_unlocked_ioctl(struct f
 
 		if (copy_from_user(&ksp, usp, sizeof(ksp)))
 			return -EFAULT;
-		rc = pkey_skey2pkey(&ksp.seckey, &ksp.protkey);
+		rc = pkey_skey2pkey(ksp.seckey.seckey, &ksp.protkey);
 		DEBUG_DBG("%s pkey_skey2pkey()=%d\n", __func__, rc);
 		if (rc)
 			break;
@@ -502,24 +887,148 @@ static long pkey_unlocked_ioctl(struct f
 	case PKEY_KBLOB2PROTK: {
 		struct pkey_kblob2pkey __user *utp = (void __user *) arg;
 		struct pkey_kblob2pkey ktp;
-		u8 __user *ukey;
 		u8 *kkey;
 
 		if (copy_from_user(&ktp, utp, sizeof(ktp)))
 			return -EFAULT;
-		if (ktp.keylen < MINKEYBLOBSIZE ||
-		    ktp.keylen > MAXKEYBLOBSIZE)
-			return -EINVAL;
-		ukey = ktp.key;
-		kkey = kmalloc(ktp.keylen, GFP_KERNEL);
-		if (kkey == NULL)
+		kkey = _copy_key_from_user(ktp.key, ktp.keylen);
+		if (IS_ERR(kkey))
+			return PTR_ERR(kkey);
+		rc = pkey_keyblob2pkey(kkey, ktp.keylen, &ktp.protkey);
+		DEBUG_DBG("%s pkey_keyblob2pkey()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc)
+			break;
+		if (copy_to_user(utp, &ktp, sizeof(ktp)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_GENSECK2: {
+		struct pkey_genseck2 __user *ugs = (void __user *) arg;
+		struct pkey_genseck2 kgs;
+		struct pkey_apqn *apqns;
+		size_t klen = KEYBLOBBUFSIZE;
+		u8 *kkey;
+
+		if (copy_from_user(&kgs, ugs, sizeof(kgs)))
+			return -EFAULT;
+		apqns = _copy_apqns_from_user(kgs.apqns, kgs.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = kmalloc(klen, GFP_KERNEL);
+		if (!kkey) {
+			kfree(apqns);
+			return -ENOMEM;
+		}
+		rc = pkey_genseckey2(apqns, kgs.apqn_entries,
+				     kgs.type, kgs.size, kgs.keygenflags,
+				     kkey, &klen);
+		DEBUG_DBG("%s pkey_genseckey2()=%d\n", __func__, rc);
+		kfree(apqns);
+		if (rc) {
+			kfree(kkey);
+			break;
+		}
+		if (kgs.key) {
+			if (kgs.keylen < klen) {
+				kfree(kkey);
+				return -EINVAL;
+			}
+			if (copy_to_user(kgs.key, kkey, klen)) {
+				kfree(kkey);
+				return -EFAULT;
+			}
+		}
+		kgs.keylen = klen;
+		if (copy_to_user(ugs, &kgs, sizeof(kgs)))
+			rc = -EFAULT;
+		kfree(kkey);
+		break;
+	}
+	case PKEY_CLR2SECK2: {
+		struct pkey_clr2seck2 __user *ucs = (void __user *) arg;
+		struct pkey_clr2seck2 kcs;
+		struct pkey_apqn *apqns;
+		size_t klen = KEYBLOBBUFSIZE;
+		u8 *kkey;
+
+		if (copy_from_user(&kcs, ucs, sizeof(kcs)))
+			return -EFAULT;
+		apqns = _copy_apqns_from_user(kcs.apqns, kcs.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = kmalloc(klen, GFP_KERNEL);
+		if (!kkey) {
+			kfree(apqns);
 			return -ENOMEM;
-		if (copy_from_user(kkey, ukey, ktp.keylen)) {
+		}
+		rc = pkey_clr2seckey2(apqns, kcs.apqn_entries,
+				      kcs.type, kcs.size, kcs.keygenflags,
+				      kcs.clrkey.clrkey, kkey, &klen);
+		DEBUG_DBG("%s pkey_clr2seckey2()=%d\n", __func__, rc);
+		kfree(apqns);
+		if (rc) {
 			kfree(kkey);
+			break;
+		}
+		if (kcs.key) {
+			if (kcs.keylen < klen) {
+				kfree(kkey);
+				return -EINVAL;
+			}
+			if (copy_to_user(kcs.key, kkey, klen)) {
+				kfree(kkey);
+				return -EFAULT;
+			}
+		}
+		kcs.keylen = klen;
+		if (copy_to_user(ucs, &kcs, sizeof(kcs)))
+			rc = -EFAULT;
+		memzero_explicit(&kcs, sizeof(kcs));
+		kfree(kkey);
+		break;
+	}
+	case PKEY_VERIFYKEY2: {
+		struct pkey_verifykey2 __user *uvk = (void __user *) arg;
+		struct pkey_verifykey2 kvk;
+		u8 *kkey;
+
+		if (copy_from_user(&kvk, uvk, sizeof(kvk)))
+			return -EFAULT;
+		kkey = _copy_key_from_user(kvk.key, kvk.keylen);
+		if (IS_ERR(kkey))
+			return PTR_ERR(kkey);
+		rc = pkey_verifykey2(kkey, kvk.keylen,
+				     &kvk.cardnr, &kvk.domain,
+				     &kvk.type, &kvk.size, &kvk.flags);
+		DEBUG_DBG("%s pkey_verifykey2()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc)
+			break;
+		if (copy_to_user(uvk, &kvk, sizeof(kvk)))
+			return -EFAULT;
+		break;
+	}
+	case PKEY_KBLOB2PROTK2: {
+		struct pkey_kblob2pkey2 __user *utp = (void __user *) arg;
+		struct pkey_kblob2pkey2 ktp;
+		struct pkey_apqn *apqns = NULL;
+		u8 *kkey;
+
+		if (copy_from_user(&ktp, utp, sizeof(ktp)))
 			return -EFAULT;
+		apqns = _copy_apqns_from_user(ktp.apqns, ktp.apqn_entries);
+		if (IS_ERR(apqns))
+			return PTR_ERR(apqns);
+		kkey = _copy_key_from_user(ktp.key, ktp.keylen);
+		if (IS_ERR(kkey)) {
+			kfree(apqns);
+			return PTR_ERR(kkey);
 		}
-		rc = pkey_keyblob2pkey(kkey, ktp.keylen, &ktp.protkey);
-		DEBUG_DBG("%s pkey_keyblob2pkey()=%d\n", __func__, rc);
+		rc = pkey_keyblob2pkey2(apqns, ktp.apqn_entries,
+					kkey, ktp.keylen, &ktp.protkey);
+		DEBUG_DBG("%s pkey_keyblob2pkey2()=%d\n", __func__, rc);
+		kfree(apqns);
 		kfree(kkey);
 		if (rc)
 			break;
@@ -527,6 +1036,97 @@ static long pkey_unlocked_ioctl(struct f
 			return -EFAULT;
 		break;
 	}
+	case PKEY_APQNS4K: {
+		struct pkey_apqns4key __user *uak = (void __user *) arg;
+		struct pkey_apqns4key kak;
+		struct pkey_apqn *apqns = NULL;
+		size_t nr_apqns, len;
+		u8 *kkey;
+
+		if (copy_from_user(&kak, uak, sizeof(kak)))
+			return -EFAULT;
+		nr_apqns = kak.apqn_entries;
+		if (nr_apqns) {
+			apqns = kmalloc_array(nr_apqns,
+					      sizeof(struct pkey_apqn),
+					      GFP_KERNEL);
+			if (!apqns)
+				return -ENOMEM;
+		}
+		kkey = _copy_key_from_user(kak.key, kak.keylen);
+		if (IS_ERR(kkey)) {
+			kfree(apqns);
+			return PTR_ERR(kkey);
+		}
+		rc = pkey_apqns4key(kkey, kak.keylen, kak.flags,
+				    apqns, &nr_apqns);
+		DEBUG_DBG("%s pkey_apqns4key()=%d\n", __func__, rc);
+		kfree(kkey);
+		if (rc && rc != -ENOSPC) {
+			kfree(apqns);
+			break;
+		}
+		if (!rc && kak.apqns) {
+			if (nr_apqns > kak.apqn_entries) {
+				kfree(apqns);
+				return -EINVAL;
+			}
+			len = nr_apqns * sizeof(struct pkey_apqn);
+			if (len) {
+				if (copy_to_user(kak.apqns, apqns, len)) {
+					kfree(apqns);
+					return -EFAULT;
+				}
+			}
+		}
+		kak.apqn_entries = nr_apqns;
+		if (copy_to_user(uak, &kak, sizeof(kak)))
+			rc = -EFAULT;
+		kfree(apqns);
+		break;
+	}
+	case PKEY_APQNS4KT: {
+		struct pkey_apqns4keytype __user *uat = (void __user *) arg;
+		struct pkey_apqns4keytype kat;
+		struct pkey_apqn *apqns = NULL;
+		size_t nr_apqns, len;
+
+		if (copy_from_user(&kat, uat, sizeof(kat)))
+			return -EFAULT;
+		nr_apqns = kat.apqn_entries;
+		if (nr_apqns) {
+			apqns = kmalloc_array(nr_apqns,
+					      sizeof(struct pkey_apqn),
+					      GFP_KERNEL);
+			if (!apqns)
+				return -ENOMEM;
+		}
+		rc = pkey_apqns4keytype(kat.type, kat.cur_mkvp, kat.alt_mkvp,
+					kat.flags, apqns, &nr_apqns);
+		DEBUG_DBG("%s pkey_apqns4keytype()=%d\n", __func__, rc);
+		if (rc && rc != -ENOSPC) {
+			kfree(apqns);
+			break;
+		}
+		if (!rc && kat.apqns) {
+			if (nr_apqns > kat.apqn_entries) {
+				kfree(apqns);
+				return -EINVAL;
+			}
+			len = nr_apqns * sizeof(struct pkey_apqn);
+			if (len) {
+				if (copy_to_user(kat.apqns, apqns, len)) {
+					kfree(apqns);
+					return -EFAULT;
+				}
+			}
+		}
+		kat.apqn_entries = nr_apqns;
+		if (copy_to_user(uat, &kat, sizeof(kat)))
+			rc = -EFAULT;
+		kfree(apqns);
+		break;
+	}
 	default:
 		/* unknown/unsupported ioctl cmd */
 		return -ENOTTY;
--- a/drivers/s390/crypto/zcrypt_ccamisc.c
+++ b/drivers/s390/crypto/zcrypt_ccamisc.c
@@ -270,7 +270,7 @@ static inline int _zcrypt_send_cprb(stru
  * Generate (random) CCA AES DATA secure key.
  */
 int cca_genseckey(u16 cardnr, u16 domain,
-		  u32 keytype, u8 seckey[SECKEYBLOBSIZE])
+		  u32 keybitsize, u8 seckey[SECKEYBLOBSIZE])
 {
 	int i, rc, keysize;
 	int seckeysize;
@@ -325,22 +325,25 @@ int cca_genseckey(u16 cardnr, u16 domain
 	preqparm->rule_array_len = sizeof(preqparm->rule_array_len);
 	preqparm->lv1.len = sizeof(struct lv1);
 	memcpy(preqparm->lv1.key_form,	 "OP      ", 8);
-	switch (keytype) {
-	case PKEY_KEYTYPE_AES_128:
+	switch (keybitsize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_KEYTYPE_AES_128: /* older ioctls used this */
 		keysize = 16;
 		memcpy(preqparm->lv1.key_length, "KEYLN16 ", 8);
 		break;
-	case PKEY_KEYTYPE_AES_192:
+	case PKEY_SIZE_AES_192:
+	case PKEY_KEYTYPE_AES_192: /* older ioctls used this */
 		keysize = 24;
 		memcpy(preqparm->lv1.key_length, "KEYLN24 ", 8);
 		break;
-	case PKEY_KEYTYPE_AES_256:
+	case PKEY_SIZE_AES_256:
+	case PKEY_KEYTYPE_AES_256: /* older ioctls used this */
 		keysize = 32;
 		memcpy(preqparm->lv1.key_length, "KEYLN32 ", 8);
 		break;
 	default:
-		DEBUG_ERR("%s unknown/unsupported keytype %d\n",
-			  __func__, keytype);
+		DEBUG_ERR("%s unknown/unsupported keybitsize %d\n",
+			  __func__, keybitsize);
 		rc = -EINVAL;
 		goto out;
 	}
@@ -408,7 +411,7 @@ EXPORT_SYMBOL(cca_genseckey);
 /*
  * Generate an CCA AES DATA secure key with given key value.
  */
-int cca_clr2seckey(u16 cardnr, u16 domain, u32 keytype,
+int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
 		   const u8 *clrkey, u8 seckey[SECKEYBLOBSIZE])
 {
 	int rc, keysize, seckeysize;
@@ -462,19 +465,22 @@ int cca_clr2seckey(u16 cardnr, u16 domai
 	memcpy(preqparm->rule_array, "AES     ", 8);
 	preqparm->rule_array_len =
 		sizeof(preqparm->rule_array_len) + sizeof(preqparm->rule_array);
-	switch (keytype) {
-	case PKEY_KEYTYPE_AES_128:
+	switch (keybitsize) {
+	case PKEY_SIZE_AES_128:
+	case PKEY_KEYTYPE_AES_128: /* older ioctls used this */
 		keysize = 16;
 		break;
-	case PKEY_KEYTYPE_AES_192:
+	case PKEY_SIZE_AES_192:
+	case PKEY_KEYTYPE_AES_192: /* older ioctls used this */
 		keysize = 24;
 		break;
-	case PKEY_KEYTYPE_AES_256:
+	case PKEY_SIZE_AES_256:
+	case PKEY_KEYTYPE_AES_256: /* older ioctls used this */
 		keysize = 32;
 		break;
 	default:
-		DEBUG_ERR("%s unknown/unsupported keytype %d\n",
-			  __func__, keytype);
+		DEBUG_ERR("%s unknown/unsupported keybitsize %d\n",
+			  __func__, keybitsize);
 		rc = -EINVAL;
 		goto out;
 	}
@@ -545,8 +551,7 @@ EXPORT_SYMBOL(cca_clr2seckey);
  */
 int cca_sec2protkey(u16 cardnr, u16 domain,
 		    const u8 seckey[SECKEYBLOBSIZE],
-		    u8 *protkey, u32 *protkeylen,
-		    u32 *keytype)
+		    u8 *protkey, u32 *protkeylen, u32 *protkeytype)
 {
 	int rc;
 	u8 *mem;
@@ -656,21 +661,21 @@ int cca_sec2protkey(u16 cardnr, u16 doma
 	switch (prepparm->lv3.keyblock.len) {
 	case 16+32:
 		/* AES 128 protected key */
-		if (keytype)
-			*keytype = PKEY_KEYTYPE_AES_128;
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_128;
 		break;
 	case 24+32:
 		/* AES 192 protected key */
-		if (keytype)
-			*keytype = PKEY_KEYTYPE_AES_192;
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_192;
 		break;
 	case 32+32:
 		/* AES 256 protected key */
-		if (keytype)
-			*keytype = PKEY_KEYTYPE_AES_256;
+		if (protkeytype)
+			*protkeytype = PKEY_KEYTYPE_AES_256;
 		break;
 	default:
-		DEBUG_ERR("%s unknown/unsupported keytype %d\n",
+		DEBUG_ERR("%s unknown/unsupported keylen %d\n",
 			  __func__, prepparm->lv3.keyblock.len);
 		rc = -EIO;
 		goto out;
@@ -1645,6 +1650,7 @@ static int findcard(u64 mkvp, u16 *pcard
 int cca_findcard(const u8 *key, u16 *pcardnr, u16 *pdomain, int verify)
 {
 	u64 mkvp;
+	int minhwtype = 0;
 	const struct keytoken_header *hdr = (struct keytoken_header *) key;
 
 	if (hdr->type != TOKTYPE_CCA_INTERNAL)
@@ -1654,11 +1660,15 @@ int cca_findcard(const u8 *key, u16 *pca
 	case TOKVER_CCA_AES:
 		mkvp = ((struct secaeskeytoken *)key)->mkvp;
 		break;
+	case TOKVER_CCA_VLSC:
+		mkvp = ((struct cipherkeytoken *)key)->mkvp0;
+		minhwtype = AP_DEVICE_TYPE_CEX6;
+		break;
 	default:
 		return -EINVAL;
 	}
 
-	return findcard(mkvp, pcardnr, pdomain, verify, 0);
+	return findcard(mkvp, pcardnr, pdomain, verify, minhwtype);
 }
 EXPORT_SYMBOL(cca_findcard);
 
--- a/drivers/s390/crypto/zcrypt_ccamisc.h
+++ b/drivers/s390/crypto/zcrypt_ccamisc.h
@@ -124,12 +124,12 @@ int cca_check_secaescipherkey(debug_info
 /*
  * Generate (random) CCA AES DATA secure key.
  */
-int cca_genseckey(u16 cardnr, u16 domain, u32 keytype, u8 *seckey);
+int cca_genseckey(u16 cardnr, u16 domain, u32 keybitsize, u8 *seckey);
 
 /*
  * Generate CCA AES DATA secure key with given clear key value.
  */
-int cca_clr2seckey(u16 cardnr, u16 domain, u32 keytype,
+int cca_clr2seckey(u16 cardnr, u16 domain, u32 keybitsize,
 		   const u8 *clrkey, u8 *seckey);
 
 /*
@@ -137,8 +137,7 @@ int cca_clr2seckey(u16 cardnr, u16 domai
  */
 int cca_sec2protkey(u16 cardnr, u16 domain,
 		    const u8 seckey[SECKEYBLOBSIZE],
-		    u8 *protkey, u32 *protkeylen,
-		    u32 *protkeytype);
+		    u8 *protkey, u32 *protkeylen, u32 *protkeytype);
 
 /*
  * Generate (random) CCA AES CIPHER secure key.
@@ -169,6 +168,7 @@ int cca_query_crypto_facility(u16 cardnr
 /*
  * Search for a matching crypto card based on the Master Key
  * Verification Pattern provided inside a secure key.
+ * Works with CCA AES data and cipher keys.
  * Returns < 0 on failure, 0 if CURRENT MKVP matches and
  * 1 if OLD MKVP matches.
  */