mirror of
https://github.com/torvalds/linux.git
synced 2024-11-25 13:41:51 +00:00
55e055ba7f
Move include statement for zcrypt_api.h from the codefiles to the zcrypt_ccamis.h header file. Signed-off-by: Harald Freudenberger <freude@linux.ibm.com> Suggested-by: Holger Dengler <dengler@linux.ibm.com> Reviewed-by: Holger Dengler <dengler@linux.ibm.com> Signed-off-by: Heiko Carstens <hca@linux.ibm.com>
578 lines
15 KiB
C
578 lines
15 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*
|
|
* pkey ep11 specific code
|
|
*
|
|
* Copyright IBM Corp. 2024
|
|
*/
|
|
|
|
#define KMSG_COMPONENT "pkey"
|
|
#define pr_fmt(fmt) KMSG_COMPONENT ": " fmt
|
|
|
|
#include <linux/init.h>
|
|
#include <linux/module.h>
|
|
#include <linux/cpufeature.h>
|
|
|
|
#include "zcrypt_ccamisc.h"
|
|
#include "zcrypt_ep11misc.h"
|
|
#include "pkey_base.h"
|
|
|
|
MODULE_LICENSE("GPL");
|
|
MODULE_AUTHOR("IBM Corporation");
|
|
MODULE_DESCRIPTION("s390 protected key EP11 handler");
|
|
|
|
#if IS_MODULE(CONFIG_PKEY_EP11)
|
|
static struct ap_device_id pkey_ep11_card_ids[] = {
|
|
{ .dev_type = AP_DEVICE_TYPE_CEX4 },
|
|
{ .dev_type = AP_DEVICE_TYPE_CEX5 },
|
|
{ .dev_type = AP_DEVICE_TYPE_CEX6 },
|
|
{ .dev_type = AP_DEVICE_TYPE_CEX7 },
|
|
{ .dev_type = AP_DEVICE_TYPE_CEX8 },
|
|
{ /* end of list */ },
|
|
};
|
|
MODULE_DEVICE_TABLE(ap, pkey_ep11_card_ids);
|
|
#endif
|
|
|
|
/*
|
|
* Check key blob for known and supported EP11 key.
|
|
*/
|
|
static bool is_ep11_key(const u8 *key, u32 keylen)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return false;
|
|
|
|
switch (hdr->type) {
|
|
case TOKTYPE_NON_CCA:
|
|
switch (hdr->version) {
|
|
case TOKVER_EP11_AES:
|
|
case TOKVER_EP11_AES_WITH_HEADER:
|
|
case TOKVER_EP11_ECC_WITH_HEADER:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static bool is_ep11_keytype(enum pkey_key_type key_type)
|
|
{
|
|
switch (key_type) {
|
|
case PKEY_TYPE_EP11:
|
|
case PKEY_TYPE_EP11_AES:
|
|
case PKEY_TYPE_EP11_ECC:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int ep11_apqns4key(const u8 *key, u32 keylen, u32 flags,
|
|
struct pkey_apqn *apqns, size_t *nr_apqns)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
u32 _nr_apqns, *_apqns = NULL;
|
|
int rc;
|
|
|
|
if (!flags)
|
|
flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
|
|
|
if (keylen < sizeof(struct keytoken_header) || flags == 0)
|
|
return -EINVAL;
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (hdr->type == TOKTYPE_NON_CCA &&
|
|
(hdr->version == TOKVER_EP11_AES_WITH_HEADER ||
|
|
hdr->version == TOKVER_EP11_ECC_WITH_HEADER) &&
|
|
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
|
|
struct ep11keyblob *kb = (struct ep11keyblob *)
|
|
(key + sizeof(struct ep11kblob_header));
|
|
int minhwtype = 0, api = 0;
|
|
|
|
if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
|
|
return -EINVAL;
|
|
if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
|
|
minhwtype = ZCRYPT_CEX7;
|
|
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
|
|
}
|
|
rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
|
|
minhwtype, api, kb->wkvp);
|
|
if (rc)
|
|
goto out;
|
|
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES &&
|
|
is_ep11_keyblob(key)) {
|
|
struct ep11keyblob *kb = (struct ep11keyblob *)key;
|
|
int minhwtype = 0, api = 0;
|
|
|
|
if (flags != PKEY_FLAGS_MATCH_CUR_MKVP)
|
|
return -EINVAL;
|
|
if (kb->attr & EP11_BLOB_PKEY_EXTRACTABLE) {
|
|
minhwtype = ZCRYPT_CEX7;
|
|
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
|
|
}
|
|
rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
|
|
minhwtype, api, kb->wkvp);
|
|
if (rc)
|
|
goto out;
|
|
|
|
} else {
|
|
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
|
|
__func__, hdr->type, hdr->version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (apqns) {
|
|
if (*nr_apqns < _nr_apqns)
|
|
rc = -ENOSPC;
|
|
else
|
|
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
|
|
}
|
|
*nr_apqns = _nr_apqns;
|
|
|
|
out:
|
|
kfree(_apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int ep11_apqns4type(enum pkey_key_type ktype,
|
|
u8 cur_mkvp[32], u8 alt_mkvp[32], u32 flags,
|
|
struct pkey_apqn *apqns, size_t *nr_apqns)
|
|
{
|
|
u32 _nr_apqns, *_apqns = NULL;
|
|
int rc;
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (ktype == PKEY_TYPE_EP11 ||
|
|
ktype == PKEY_TYPE_EP11_AES ||
|
|
ktype == PKEY_TYPE_EP11_ECC) {
|
|
u8 *wkvp = NULL;
|
|
int api;
|
|
|
|
if (flags & PKEY_FLAGS_MATCH_CUR_MKVP)
|
|
wkvp = cur_mkvp;
|
|
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
|
|
rc = ep11_findcard2(&_apqns, &_nr_apqns, 0xFFFF, 0xFFFF,
|
|
ZCRYPT_CEX7, api, wkvp);
|
|
if (rc)
|
|
goto out;
|
|
|
|
} else {
|
|
PKEY_DBF_ERR("%s unknown/unsupported key type %d\n",
|
|
__func__, (int)ktype);
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (apqns) {
|
|
if (*nr_apqns < _nr_apqns)
|
|
rc = -ENOSPC;
|
|
else
|
|
memcpy(apqns, _apqns, _nr_apqns * sizeof(u32));
|
|
}
|
|
*nr_apqns = _nr_apqns;
|
|
|
|
out:
|
|
kfree(_apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int ep11_key2protkey(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
const u8 *key, u32 keylen,
|
|
u8 *protkey, u32 *protkeylen, u32 *protkeytype)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
struct pkey_apqn *local_apqns = NULL;
|
|
int i, rc;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return -EINVAL;
|
|
|
|
if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
|
|
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
|
|
/* EP11 AES key blob with header */
|
|
if (ep11_check_aes_key_with_hdr(pkey_dbf_info,
|
|
3, key, keylen, 1))
|
|
return -EINVAL;
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
|
|
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
|
|
/* EP11 ECC key blob with header */
|
|
if (ep11_check_ecc_key_with_hdr(pkey_dbf_info,
|
|
3, key, keylen, 1))
|
|
return -EINVAL;
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES &&
|
|
is_ep11_keyblob(key)) {
|
|
/* EP11 AES key blob with header in session field */
|
|
if (ep11_check_aes_key(pkey_dbf_info, 3, key, keylen, 1))
|
|
return -EINVAL;
|
|
} else {
|
|
PKEY_DBF_ERR("%s unknown/unsupported blob type %d version %d\n",
|
|
__func__, hdr->type, hdr->version);
|
|
return -EINVAL;
|
|
}
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (!apqns || (nr_apqns == 1 &&
|
|
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
|
|
nr_apqns = MAXAPQNSINLIST;
|
|
local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
|
|
GFP_KERNEL);
|
|
if (!local_apqns)
|
|
return -ENOMEM;
|
|
rc = ep11_apqns4key(key, keylen, 0, local_apqns, &nr_apqns);
|
|
if (rc)
|
|
goto out;
|
|
apqns = local_apqns;
|
|
}
|
|
|
|
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
|
|
if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES_WITH_HEADER &&
|
|
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
|
|
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
|
|
key, hdr->len, protkey,
|
|
protkeylen, protkeytype);
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_ECC_WITH_HEADER &&
|
|
is_ep11_keyblob(key + sizeof(struct ep11kblob_header))) {
|
|
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
|
|
key, hdr->len, protkey,
|
|
protkeylen, protkeytype);
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES &&
|
|
is_ep11_keyblob(key)) {
|
|
rc = ep11_kblob2protkey(apqns[i].card, apqns[i].domain,
|
|
key, hdr->len, protkey,
|
|
protkeylen, protkeytype);
|
|
} else {
|
|
rc = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
out:
|
|
kfree(local_apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Generate EP11 secure key.
|
|
* As of now only EP11 AES secure keys are supported.
|
|
* keytype is one of the PKEY_KEYTYPE_* constants,
|
|
* subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
|
|
* or 0 (results in subtype PKEY_TYPE_EP11_AES),
|
|
* keybitsize is the bit size of the key (may be 0 for
|
|
* keytype PKEY_KEYTYPE_AES_*).
|
|
*/
|
|
static int ep11_gen_key(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
u32 keytype, u32 subtype,
|
|
u32 keybitsize, u32 flags,
|
|
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo)
|
|
{
|
|
struct pkey_apqn *local_apqns = NULL;
|
|
int i, len, rc;
|
|
|
|
/* check keytype, subtype, keybitsize */
|
|
switch (keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
len = pkey_keytype_aes_to_size(keytype);
|
|
if (keybitsize && keybitsize != 8 * len) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
|
|
__func__, keybitsize);
|
|
return -EINVAL;
|
|
}
|
|
keybitsize = 8 * len;
|
|
switch (subtype) {
|
|
case PKEY_TYPE_EP11:
|
|
case PKEY_TYPE_EP11_AES:
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
|
|
__func__, subtype);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
|
|
__func__, keytype);
|
|
return -EINVAL;
|
|
}
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (!apqns || (nr_apqns == 1 &&
|
|
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
|
|
nr_apqns = MAXAPQNSINLIST;
|
|
local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
|
|
GFP_KERNEL);
|
|
if (!local_apqns)
|
|
return -ENOMEM;
|
|
rc = ep11_apqns4type(subtype, NULL, NULL, 0,
|
|
local_apqns, &nr_apqns);
|
|
if (rc)
|
|
goto out;
|
|
apqns = local_apqns;
|
|
}
|
|
|
|
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
|
|
rc = ep11_genaeskey(apqns[i].card, apqns[i].domain,
|
|
keybitsize, flags,
|
|
keybuf, keybuflen, subtype);
|
|
}
|
|
|
|
out:
|
|
kfree(local_apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* Generate EP11 secure key with given clear key value.
|
|
* As of now only EP11 AES secure keys are supported.
|
|
* keytype is one of the PKEY_KEYTYPE_* constants,
|
|
* subtype may be PKEY_TYPE_EP11 or PKEY_TYPE_EP11_AES
|
|
* or 0 (assumes PKEY_TYPE_EP11_AES then).
|
|
* keybitsize is the bit size of the key (may be 0 for
|
|
* keytype PKEY_KEYTYPE_AES_*).
|
|
*/
|
|
static int ep11_clr2key(const struct pkey_apqn *apqns, size_t nr_apqns,
|
|
u32 keytype, u32 subtype,
|
|
u32 keybitsize, u32 flags,
|
|
const u8 *clrkey, u32 clrkeylen,
|
|
u8 *keybuf, u32 *keybuflen, u32 *_keyinfo)
|
|
{
|
|
struct pkey_apqn *local_apqns = NULL;
|
|
int i, len, rc;
|
|
|
|
/* check keytype, subtype, clrkeylen, keybitsize */
|
|
switch (keytype) {
|
|
case PKEY_KEYTYPE_AES_128:
|
|
case PKEY_KEYTYPE_AES_192:
|
|
case PKEY_KEYTYPE_AES_256:
|
|
len = pkey_keytype_aes_to_size(keytype);
|
|
if (keybitsize && keybitsize != 8 * len) {
|
|
PKEY_DBF_ERR("%s unknown/unsupported keybitsize %d\n",
|
|
__func__, keybitsize);
|
|
return -EINVAL;
|
|
}
|
|
keybitsize = 8 * len;
|
|
if (clrkeylen != len) {
|
|
PKEY_DBF_ERR("%s invalid clear key len %d != %d\n",
|
|
__func__, clrkeylen, len);
|
|
return -EINVAL;
|
|
}
|
|
switch (subtype) {
|
|
case PKEY_TYPE_EP11:
|
|
case PKEY_TYPE_EP11_AES:
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported subtype %d\n",
|
|
__func__, subtype);
|
|
return -EINVAL;
|
|
}
|
|
break;
|
|
default:
|
|
PKEY_DBF_ERR("%s unknown/unsupported keytype %d\n",
|
|
__func__, keytype);
|
|
return -EINVAL;
|
|
}
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (!apqns || (nr_apqns == 1 &&
|
|
apqns[0].card == 0xFFFF && apqns[0].domain == 0xFFFF)) {
|
|
nr_apqns = MAXAPQNSINLIST;
|
|
local_apqns = kmalloc_array(nr_apqns, sizeof(struct pkey_apqn),
|
|
GFP_KERNEL);
|
|
if (!local_apqns)
|
|
return -ENOMEM;
|
|
rc = ep11_apqns4type(subtype, NULL, NULL, 0,
|
|
local_apqns, &nr_apqns);
|
|
if (rc)
|
|
goto out;
|
|
apqns = local_apqns;
|
|
}
|
|
|
|
for (rc = -ENODEV, i = 0; rc && i < nr_apqns; i++) {
|
|
rc = ep11_clr2keyblob(apqns[i].card, apqns[i].domain,
|
|
keybitsize, flags, clrkey,
|
|
keybuf, keybuflen, subtype);
|
|
}
|
|
|
|
out:
|
|
kfree(local_apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
static int ep11_verifykey(const u8 *key, u32 keylen,
|
|
u16 *card, u16 *dom,
|
|
u32 *keytype, u32 *keybitsize, u32 *flags)
|
|
{
|
|
struct keytoken_header *hdr = (struct keytoken_header *)key;
|
|
u32 nr_apqns, *apqns = NULL;
|
|
int rc;
|
|
|
|
if (keylen < sizeof(*hdr))
|
|
return -EINVAL;
|
|
|
|
zcrypt_wait_api_operational();
|
|
|
|
if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES) {
|
|
struct ep11keyblob *kb = (struct ep11keyblob *)key;
|
|
int api;
|
|
|
|
rc = ep11_check_aes_key(pkey_dbf_info, 3, key, keylen, 1);
|
|
if (rc)
|
|
goto out;
|
|
*keytype = PKEY_TYPE_EP11;
|
|
*keybitsize = kb->head.bitlen;
|
|
|
|
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
|
|
rc = ep11_findcard2(&apqns, &nr_apqns, *card, *dom,
|
|
ZCRYPT_CEX7, api,
|
|
ep11_kb_wkvp(key, keylen));
|
|
if (rc)
|
|
goto out;
|
|
|
|
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
|
|
|
*card = ((struct pkey_apqn *)apqns)->card;
|
|
*dom = ((struct pkey_apqn *)apqns)->domain;
|
|
|
|
} else if (hdr->type == TOKTYPE_NON_CCA &&
|
|
hdr->version == TOKVER_EP11_AES_WITH_HEADER) {
|
|
struct ep11kblob_header *kh = (struct ep11kblob_header *)key;
|
|
int api;
|
|
|
|
rc = ep11_check_aes_key_with_hdr(pkey_dbf_info,
|
|
3, key, keylen, 1);
|
|
if (rc)
|
|
goto out;
|
|
*keytype = PKEY_TYPE_EP11_AES;
|
|
*keybitsize = kh->bitlen;
|
|
|
|
api = ap_is_se_guest() ? EP11_API_V6 : EP11_API_V4;
|
|
rc = ep11_findcard2(&apqns, &nr_apqns, *card, *dom,
|
|
ZCRYPT_CEX7, api,
|
|
ep11_kb_wkvp(key, keylen));
|
|
if (rc)
|
|
goto out;
|
|
|
|
*flags = PKEY_FLAGS_MATCH_CUR_MKVP;
|
|
|
|
*card = ((struct pkey_apqn *)apqns)->card;
|
|
*dom = ((struct pkey_apqn *)apqns)->domain;
|
|
|
|
} else {
|
|
/* unknown/unsupported key blob */
|
|
rc = -EINVAL;
|
|
}
|
|
|
|
out:
|
|
kfree(apqns);
|
|
pr_debug("rc=%d\n", rc);
|
|
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,
|
|
.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,
|
|
};
|
|
|
|
/*
|
|
* Module init
|
|
*/
|
|
static int __init pkey_ep11_init(void)
|
|
{
|
|
/* register this module as pkey handler for all the ep11 stuff */
|
|
return pkey_handler_register(&ep11_handler);
|
|
}
|
|
|
|
/*
|
|
* Module exit
|
|
*/
|
|
static void __exit pkey_ep11_exit(void)
|
|
{
|
|
/* unregister this module as pkey handler */
|
|
pkey_handler_unregister(&ep11_handler);
|
|
}
|
|
|
|
module_init(pkey_ep11_init);
|
|
module_exit(pkey_ep11_exit);
|