Commit 2fc401b9 authored by Harald Freudenberger's avatar Harald Freudenberger Committed by Vasily Gorbik

s390/pkey: Add slowpath function to CCA and EP11 handler

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: default avatarHarald Freudenberger <freude@linux.ibm.com>
Reviewed-by: default avatarHolger Dengler <dengler@linux.ibm.com>
Signed-off-by: default avatarVasily Gorbik <gor@linux.ibm.com>
parent 8fcc231c
......@@ -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;
}
......
......@@ -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,
......
......@@ -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,
......@@ -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,
......
......@@ -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,
};
/*
......
......@@ -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,
};
/*
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment