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

#define pr_fmt(fmt) "pkey: " fmt

#include <linux/cpufeature.h>
#include <linux/init.h>
#include <linux/module.h>
#include <asm/uv.h>

#include "zcrypt_ccamisc.h"
#include "pkey_base.h"

MODULE_LICENSE("GPL");
MODULE_AUTHOR("IBM Corporation");
MODULE_DESCRIPTION("s390 protected key UV handler");

/*
 * One pre-allocated uv_secret_list for use with uv_find_secret()
 */
static struct uv_secret_list *uv_list;
static DEFINE_MUTEX(uv_list_mutex);

/*
 * UV secret token struct and defines.
 */

#define TOKVER_UV_SECRET 0x09

struct uvsecrettoken {
        u8  type;               /* 0x00 = TOKTYPE_NON_CCA */
        u8  res0[3];
        u8  version;            /* 0x09 = TOKVER_UV_SECRET */
        u8  res1[3];
        u16 secret_type;        /* one of enum uv_secret_types from uv.h */
        u16 secret_len;         /* length in bytes of the secret */
        u8  secret_id[UV_SECRET_ID_LEN]; /* the secret id for this secret */
} __packed;

/*
 * Check key blob for known and supported UV key.
 */
static bool is_uv_key(const u8 *key, u32 keylen)
{
        struct uvsecrettoken *t = (struct uvsecrettoken *)key;

        if (keylen < sizeof(*t))
                return false;

        switch (t->type) {
        case TOKTYPE_NON_CCA:
                switch (t->version) {
                case TOKVER_UV_SECRET:
                        switch (t->secret_type) {
                        case UV_SECRET_AES_128:
                        case UV_SECRET_AES_192:
                        case UV_SECRET_AES_256:
                        case UV_SECRET_AES_XTS_128:
                        case UV_SECRET_AES_XTS_256:
                        case UV_SECRET_HMAC_SHA_256:
                        case UV_SECRET_HMAC_SHA_512:
                        case UV_SECRET_ECDSA_P256:
                        case UV_SECRET_ECDSA_P384:
                        case UV_SECRET_ECDSA_P521:
                        case UV_SECRET_ECDSA_ED25519:
                        case UV_SECRET_ECDSA_ED448:
                                return true;
                        default:
                                return false;
                        }
                default:
                        return false;
                }
        default:
                return false;
        }
}

static bool is_uv_keytype(enum pkey_key_type keytype)
{
        switch (keytype) {
        case PKEY_TYPE_UVSECRET:
                return true;
        default:
                return false;
        }
}

static int get_secret_metadata(const u8 secret_id[UV_SECRET_ID_LEN],
                               struct uv_secret_list_item_hdr *secret)
{
        int rc;

        mutex_lock(&uv_list_mutex);
        memset(uv_list, 0, sizeof(*uv_list));
        rc = uv_find_secret(secret_id, uv_list, secret);
        mutex_unlock(&uv_list_mutex);

        return rc;
}

static int retrieve_secret(const u8 secret_id[UV_SECRET_ID_LEN],
                           u16 *secret_type, u8 *buf, u32 *buflen)
{
        struct uv_secret_list_item_hdr secret_meta_data;
        int rc;

        rc = get_secret_metadata(secret_id, &secret_meta_data);
        if (rc)
                return rc;

        if (*buflen < secret_meta_data.length)
                return -EINVAL;

        rc = uv_retrieve_secret(secret_meta_data.index,
                                buf, secret_meta_data.length);
        if (rc)
                return rc;

        *secret_type = secret_meta_data.type;
        *buflen = secret_meta_data.length;

        return 0;
}

static int uv_get_size_and_type(u16 secret_type, u32 *pkeysize, u32 *pkeytype)
{
        int rc = 0;

        switch (secret_type) {
        case UV_SECRET_AES_128:
                *pkeysize = 16 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_AES_128;
                break;
        case UV_SECRET_AES_192:
                *pkeysize = 24 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_AES_192;
                break;
        case UV_SECRET_AES_256:
                *pkeysize = 32 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_AES_256;
                break;
        case UV_SECRET_AES_XTS_128:
                *pkeysize = 16 + 16 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_AES_XTS_128;
                break;
        case UV_SECRET_AES_XTS_256:
                *pkeysize = 32 + 32 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_AES_XTS_256;
                break;
        case UV_SECRET_HMAC_SHA_256:
                *pkeysize = 64 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_HMAC_512;
                break;
        case UV_SECRET_HMAC_SHA_512:
                *pkeysize = 128 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_HMAC_1024;
                break;
        case UV_SECRET_ECDSA_P256:
                *pkeysize = 32 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_ECC_P256;
                break;
        case UV_SECRET_ECDSA_P384:
                *pkeysize = 48 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_ECC_P384;
                break;
        case UV_SECRET_ECDSA_P521:
                *pkeysize = 80 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_ECC_P521;
                break;
        case UV_SECRET_ECDSA_ED25519:
                *pkeysize = 32 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_ECC_ED25519;
                break;
        case UV_SECRET_ECDSA_ED448:
                *pkeysize = 64 + AES_WK_VP_SIZE;
                *pkeytype = PKEY_KEYTYPE_ECC_ED448;
                break;
        default:
                rc = -EINVAL;
        }

        return rc;
}

static int uv_key2protkey(const struct pkey_apqn *_apqns __always_unused,
                          size_t _nr_apqns __always_unused,
                          const u8 *key, u32 keylen,
                          u8 *protkey, u32 *protkeylen, u32 *keyinfo,
                          u32 _xflags __always_unused)
{
        struct uvsecrettoken *t = (struct uvsecrettoken *)key;
        u32 pkeysize, pkeytype;
        u16 secret_type;
        int rc;

        rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
        if (rc)
                goto out;

        if (*protkeylen < pkeysize) {
                PKEY_DBF_ERR("%s prot key buffer size too small: %u < %u\n",
                             __func__, *protkeylen, pkeysize);
                rc = -EINVAL;
                goto out;
        }

        rc = retrieve_secret(t->secret_id, &secret_type, protkey, protkeylen);
        if (rc) {
                PKEY_DBF_ERR("%s retrieve_secret() failed with %d\n",
                             __func__, rc);
                goto out;
        }
        if (secret_type != t->secret_type) {
                PKEY_DBF_ERR("%s retrieved secret type %u != expected type %u\n",
                             __func__, secret_type, t->secret_type);
                rc = -EINVAL;
                goto out;
        }

        if (keyinfo)
                *keyinfo = pkeytype;

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

static int uv_verifykey(const u8 *key, u32 keylen,
                        u16 *_card __always_unused,
                        u16 *_dom __always_unused,
                        u32 *keytype, u32 *keybitsize, u32 *flags,
                        u32 xflags __always_unused)
{
        struct uvsecrettoken *t = (struct uvsecrettoken *)key;
        struct uv_secret_list_item_hdr secret_meta_data;
        u32 pkeysize, pkeytype, bitsize;
        int rc;

        rc = uv_get_size_and_type(t->secret_type, &pkeysize, &pkeytype);
        if (rc)
                goto out;

        rc = get_secret_metadata(t->secret_id, &secret_meta_data);
        if (rc)
                goto out;

        if (secret_meta_data.type != t->secret_type) {
                rc = -EINVAL;
                goto out;
        }

        /* set keytype; keybitsize and flags are not supported */
        if (keytype)
                *keytype = PKEY_TYPE_UVSECRET;
        if (keybitsize) {
                bitsize = 8 * pkey_keytype_to_size(pkeytype);
                *keybitsize = bitsize ?: PKEY_SIZE_UNKNOWN;
        }
        if (flags)
                *flags = pkeytype;

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

static struct pkey_handler uv_handler = {
        .module                  = THIS_MODULE,
        .name                    = "PKEY UV handler",
        .is_supported_key        = is_uv_key,
        .is_supported_keytype    = is_uv_keytype,
        .key_to_protkey          = uv_key2protkey,
        .verify_key              = uv_verifykey,
};

/*
 * Module init
 */
static int __init pkey_uv_init(void)
{
        int rc;

        if (!is_prot_virt_guest())
                return -ENODEV;

        if (!test_bit_inv(BIT_UVC_CMD_RETR_SECRET, uv_info.inst_calls_list))
                return -ENODEV;

        uv_list = kmalloc_obj(*uv_list);
        if (!uv_list)
                return -ENOMEM;

        rc = pkey_handler_register(&uv_handler);
        if (rc)
                kfree(uv_list);

        return rc;
}

/*
 * Module exit
 */
static void __exit pkey_uv_exit(void)
{
        pkey_handler_unregister(&uv_handler);
        mutex_lock(&uv_list_mutex);
        kvfree(uv_list);
        mutex_unlock(&uv_list_mutex);
}

module_cpu_feature_match(S390_CPU_FEATURE_UV, pkey_uv_init);
module_exit(pkey_uv_exit);