root/drivers/s390/crypto/pkey_ep11.c
// SPDX-License-Identifier: GPL-2.0
/*
 *  pkey ep11 specific code
 *
 *  Copyright IBM Corp. 2024
 */

#define pr_fmt(fmt) "pkey: " 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, u32 pflags)
{
        struct keytoken_header *hdr = (struct keytoken_header *)key;
        u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
        u32 xflags;
        int rc;

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        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, xflags);
                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, xflags);
                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:
        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 pflags)
{
        u32 _apqns[MAXAPQNSINLIST], _nr_apqns = ARRAY_SIZE(_apqns);
        u32 xflags;
        int rc;

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        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, xflags);
                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:
        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,
                            u32 pflags)
{
        struct keytoken_header *hdr = (struct keytoken_header *)key;
        struct pkey_apqn _apqns[MAXAPQNSINLIST];
        u32 xflags;
        int i, rc;

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        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;
                rc = ep11_apqns4key(key, keylen, 0, _apqns, &nr_apqns, pflags);
                if (rc)
                        goto out;
                apqns = _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, xflags);
                } 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, xflags);
                } 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, xflags);
                } else {
                        rc = -EINVAL;
                        break;
                }
        }

out:
        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, u32 pflags)
{
        struct pkey_apqn _apqns[MAXAPQNSINLIST];
        int i, len, rc;
        u32 xflags;

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        /* 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;
                rc = ep11_apqns4type(subtype, NULL, NULL, 0,
                                     _apqns, &nr_apqns, pflags);
                if (rc)
                        goto out;
                apqns = _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, xflags);
        }

out:
        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, u32 pflags)
{
        struct pkey_apqn _apqns[MAXAPQNSINLIST];
        int i, len, rc;
        u32 xflags;

        if (pflags & PKEY_XFLAG_NOCLEARKEY) {
                PKEY_DBF_ERR("%s clear key but xflag NOCLEARKEY\n", __func__);
                return -EINVAL;
        }

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        /* 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;
                rc = ep11_apqns4type(subtype, NULL, NULL, 0,
                                     _apqns, &nr_apqns, pflags);
                if (rc)
                        goto out;
                apqns = _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, xflags);
        }

out:
        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, u32 pflags)
{
        struct keytoken_header *hdr = (struct keytoken_header *)key;
        u32 apqns[MAXAPQNSINLIST], nr_apqns = ARRAY_SIZE(apqns);
        u32 xflags;
        int rc;

        xflags = pflags & PKEY_XFLAG_NOMEMALLOC ? ZCRYPT_XFLAG_NOMEMALLOC : 0;

        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), xflags);
                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), xflags);
                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:
        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, u32 pflags)
{
        const struct keytoken_header *hdr = (const struct keytoken_header *)key;
        const struct clearkeytoken *t = (const struct clearkeytoken *)key;
        u8 tmpbuf[MAXEP11AESKEYBLOBSIZE]; /* 336 bytes */
        u32 tmplen, keysize = 0;
        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;

        /* 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, pflags);
                pr_debug("ep11_clr2key()=%d\n", rc);
                if (rc)
                        continue;
                rc = ep11_key2protkey(NULL, 0, tmpbuf, tmplen,
                                      protkey, protkeylen, protkeytype, pflags);
                pr_debug("ep11_key2protkey()=%d\n", rc);
        }

        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);