Skip to content

Commit

Permalink
s390/pkey: Add slowpath function to CCA and EP11 handler
Browse files Browse the repository at this point in the history
For some keys there exists an alternative but usually slower
path to convert the key material into a protected key.
This patch introduces a new handler function
  slowpath_key_to_protkey()
which provides this alternate path for the CCA and EP11
handler code. With that even the knowledge about how
and when this can be used within the pkey API code can
be removed. So now the pkey API just tries the primary
way and if that fails simple tries the alternative way.

Signed-off-by: Harald Freudenberger <[email protected]>
Reviewed-by: Holger Dengler <[email protected]>
Signed-off-by: Vasily Gorbik <[email protected]>
  • Loading branch information
hfreude authored and Vasily Gorbik committed Aug 29, 2024
1 parent 8fcc231 commit 2fc401b
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 110 deletions.
107 changes: 17 additions & 90 deletions drivers/s390/crypto/pkey_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -22,102 +22,29 @@
/*
* Helper functions
*/

static int key2protkey_fallback(const struct clearkeytoken *t,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
size_t tmpbuflen = max_t(size_t, SECKEYBLOBSIZE, MAXEP11AESKEYBLOBSIZE);
u32 keysize, keybitsize, tmplen;
u8 *tmpbuf = NULL;
int i, rc;

/* As of now only for AES keys a fallback is available */

keysize = pkey_keytype_aes_to_size(t->keytype);
if (!keysize) {
PKEY_DBF_ERR("%s unknown/unsupported keytype %u\n",
__func__, t->keytype);
return -EINVAL;
}
if (t->len != keysize) {
PKEY_DBF_ERR("%s clear key AES token: invalid key len %u\n",
__func__, t->len);
return -EINVAL;
}
keybitsize = 8 * keysize;

/* alloc tmp key buffer */
tmpbuf = kmalloc(tmpbuflen, GFP_ATOMIC);
if (!tmpbuf)
return -ENOMEM;

/* try two times in case of failure */
for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {

/* CCA secure key way */
tmplen = tmpbuflen;
rc = pkey_handler_clr_to_key(NULL, 0,
t->keytype, PKEY_TYPE_CCA_DATA,
keybitsize, 0,
t->clearkey, t->len,
tmpbuf, &tmplen, NULL);
pr_debug("clr_to_key()=%d\n", rc);
if (rc)
goto try_via_ep11;
rc = pkey_handler_key_to_protkey(NULL, 0,
tmpbuf, tmplen,
protkey, protkeylen,
protkeytype);
pr_debug("key_to_protkey()=%d\n", rc);
if (!rc)
break;

try_via_ep11:
/* the CCA way failed, try via EP11 */
tmplen = tmpbuflen;
rc = pkey_handler_clr_to_key(NULL, 0,
t->keytype, PKEY_TYPE_EP11_AES,
keybitsize, 0,
t->clearkey, t->len,
tmpbuf, &tmplen, NULL);
pr_debug("clr_to_key()=%d\n", rc);
if (rc)
continue;
rc = pkey_handler_key_to_protkey(NULL, 0,
tmpbuf, tmplen,
protkey, protkeylen,
protkeytype);
pr_debug("key_to_protkey()=%d\n", rc);
}

kfree(tmpbuf);

return rc;
}

static int key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, size_t keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
{
struct keytoken_header *hdr = (struct keytoken_header *)key;
int i, rc;

/* retry two times */
for (rc = -ENODEV, i = 0; rc && i < 2; i++) {
/* First try the direct way */
rc = pkey_handler_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype);
/* For some clear key tokens there exists a fallback way */
if (rc &&
hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_CLEAR_KEY)
rc = key2protkey_fallback((struct clearkeytoken *)key,
protkey, protkeylen,
protkeytype);
int rc;

/* try the direct way */
rc = pkey_handler_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype);

/* if this did not work, try the slowpath way */
if (rc == -ENODEV) {
rc = pkey_handler_slowpath_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype);
if (rc)
rc = -ENODEV;
}

pr_debug("rc=%d\n", rc);
return rc;
}

Expand Down
40 changes: 40 additions & 0 deletions drivers/s390/crypto/pkey_base.c
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,46 @@ int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
}
EXPORT_SYMBOL(pkey_handler_key_to_protkey);

/*
* This handler invocation is special as there may be more than
* one handler providing support for the very same key (type).
* And the handler may not respond true on is_supported_key(),
* so simple try and check return value here.
*/
int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype)
{
const struct pkey_handler *h, *htmp[10];
int i, n = 0, rc = -ENODEV;

rcu_read_lock();
list_for_each_entry_rcu(h, &handler_list, list) {
if (!try_module_get(h->module))
continue;
if (h->slowpath_key_to_protkey && n < ARRAY_SIZE(htmp))
htmp[n++] = h;
else
module_put(h->module);
}
rcu_read_unlock();

for (i = 0; i < n; i++) {
h = htmp[i];
if (rc)
rc = h->slowpath_key_to_protkey(apqns, nr_apqns,
key, keylen,
protkey, protkeylen,
protkeytype);
module_put(h->module);
}

return rc;
}
EXPORT_SYMBOL(pkey_handler_slowpath_key_to_protkey);

int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
Expand Down
10 changes: 10 additions & 0 deletions drivers/s390/crypto/pkey_base.h
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,11 @@ struct pkey_handler {
int (*key_to_protkey)(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype);
int (*slowpath_key_to_protkey)(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype);
int (*gen_key)(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
Expand Down Expand Up @@ -148,6 +153,11 @@ void pkey_handler_put(const struct pkey_handler *handler);
int pkey_handler_key_to_protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen, u32 *protkeytype);
int pkey_handler_slowpath_key_to_protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype);
int pkey_handler_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
u32 keytype, u32 keysubtype,
u32 keybitsize, u32 flags,
Expand Down
73 changes: 63 additions & 10 deletions drivers/s390/crypto/pkey_cca.c
Original file line number Diff line number Diff line change
Expand Up @@ -541,17 +541,70 @@ static int cca_verifykey(const u8 *key, u32 keylen,
return rc;
}

/*
* This function provides an alternate but usually slow way
* to convert a 'clear key token' with AES key material into
* a protected key. This is done via an intermediate step
* which creates a CCA AES DATA secure key first and then
* derives the protected key from this secure key.
*/
static int cca_slowpath_key2protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype)
{
const struct keytoken_header *hdr = (const struct keytoken_header *)key;
const struct clearkeytoken *t = (const struct clearkeytoken *)key;
u32 tmplen, keysize = 0;
u8 *tmpbuf;
int i, rc;

if (keylen < sizeof(*hdr))
return -EINVAL;

if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_CLEAR_KEY)
keysize = pkey_keytype_aes_to_size(t->keytype);
if (!keysize || t->len != keysize)
return -EINVAL;

/* alloc tmp key buffer */
tmpbuf = kmalloc(SECKEYBLOBSIZE, GFP_ATOMIC);
if (!tmpbuf)
return -ENOMEM;

/* try two times in case of failure */
for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
tmplen = SECKEYBLOBSIZE;
rc = cca_clr2key(NULL, 0, t->keytype, PKEY_TYPE_CCA_DATA,
8 * keysize, 0, t->clearkey, t->len,
tmpbuf, &tmplen, NULL);
pr_debug("cca_clr2key()=%d\n", rc);
if (rc)
continue;
rc = cca_key2protkey(NULL, 0, tmpbuf, tmplen,
protkey, protkeylen, protkeytype);
pr_debug("cca_key2protkey()=%d\n", rc);
}

kfree(tmpbuf);
pr_debug("rc=%d\n", rc);
return rc;
}

static struct pkey_handler cca_handler = {
.module = THIS_MODULE,
.name = "PKEY CCA handler",
.is_supported_key = is_cca_key,
.is_supported_keytype = is_cca_keytype,
.key_to_protkey = cca_key2protkey,
.gen_key = cca_gen_key,
.clr_to_key = cca_clr2key,
.verify_key = cca_verifykey,
.apqns_for_key = cca_apqns4key,
.apqns_for_keytype = cca_apqns4type,
.module = THIS_MODULE,
.name = "PKEY CCA handler",
.is_supported_key = is_cca_key,
.is_supported_keytype = is_cca_keytype,
.key_to_protkey = cca_key2protkey,
.slowpath_key_to_protkey = cca_slowpath_key2protkey,
.gen_key = cca_gen_key,
.clr_to_key = cca_clr2key,
.verify_key = cca_verifykey,
.apqns_for_key = cca_apqns4key,
.apqns_for_keytype = cca_apqns4type,
};

/*
Expand Down
73 changes: 63 additions & 10 deletions drivers/s390/crypto/pkey_ep11.c
Original file line number Diff line number Diff line change
Expand Up @@ -490,17 +490,70 @@ static int ep11_verifykey(const u8 *key, u32 keylen,
return rc;
}

/*
* This function provides an alternate but usually slow way
* to convert a 'clear key token' with AES key material into
* a protected key. That is done via an intermediate step
* which creates an EP11 AES secure key first and then derives
* the protected key from this secure key.
*/
static int ep11_slowpath_key2protkey(const struct pkey_apqn *apqns,
size_t nr_apqns,
const u8 *key, u32 keylen,
u8 *protkey, u32 *protkeylen,
u32 *protkeytype)
{
const struct keytoken_header *hdr = (const struct keytoken_header *)key;
const struct clearkeytoken *t = (const struct clearkeytoken *)key;
u32 tmplen, keysize = 0;
u8 *tmpbuf;
int i, rc;

if (keylen < sizeof(*hdr))
return -EINVAL;

if (hdr->type == TOKTYPE_NON_CCA &&
hdr->version == TOKVER_CLEAR_KEY)
keysize = pkey_keytype_aes_to_size(t->keytype);
if (!keysize || t->len != keysize)
return -EINVAL;

/* alloc tmp key buffer */
tmpbuf = kmalloc(MAXEP11AESKEYBLOBSIZE, GFP_ATOMIC);
if (!tmpbuf)
return -ENOMEM;

/* try two times in case of failure */
for (i = 0, rc = -ENODEV; i < 2 && rc; i++) {
tmplen = MAXEP11AESKEYBLOBSIZE;
rc = ep11_clr2key(NULL, 0, t->keytype, PKEY_TYPE_EP11,
8 * keysize, 0, t->clearkey, t->len,
tmpbuf, &tmplen, NULL);
pr_debug("ep11_clr2key()=%d\n", rc);
if (rc)
continue;
rc = ep11_key2protkey(NULL, 0, tmpbuf, tmplen,
protkey, protkeylen, protkeytype);
pr_debug("ep11_key2protkey()=%d\n", rc);
}

kfree(tmpbuf);
pr_debug("rc=%d\n", rc);
return rc;
}

static struct pkey_handler ep11_handler = {
.module = THIS_MODULE,
.name = "PKEY EP11 handler",
.is_supported_key = is_ep11_key,
.is_supported_keytype = is_ep11_keytype,
.key_to_protkey = ep11_key2protkey,
.gen_key = ep11_gen_key,
.clr_to_key = ep11_clr2key,
.verify_key = ep11_verifykey,
.apqns_for_key = ep11_apqns4key,
.apqns_for_keytype = ep11_apqns4type,
.module = THIS_MODULE,
.name = "PKEY EP11 handler",
.is_supported_key = is_ep11_key,
.is_supported_keytype = is_ep11_keytype,
.key_to_protkey = ep11_key2protkey,
.slowpath_key_to_protkey = ep11_slowpath_key2protkey,
.gen_key = ep11_gen_key,
.clr_to_key = ep11_clr2key,
.verify_key = ep11_verifykey,
.apqns_for_key = ep11_apqns4key,
.apqns_for_keytype = ep11_apqns4type,
};

/*
Expand Down

0 comments on commit 2fc401b

Please sign in to comment.