root/drivers/crypto/ccp/ccp-crypto-rsa.c
// SPDX-License-Identifier: GPL-2.0-only
/*
 * AMD Cryptographic Coprocessor (CCP) RSA crypto API support
 *
 * Copyright (C) 2017 Advanced Micro Devices, Inc.
 *
 * Author: Gary R Hook <gary.hook@amd.com>
 */

#include <linux/module.h>
#include <linux/sched.h>
#include <linux/scatterlist.h>
#include <linux/string.h>
#include <linux/crypto.h>
#include <crypto/algapi.h>
#include <crypto/internal/rsa.h>
#include <crypto/internal/akcipher.h>
#include <crypto/akcipher.h>
#include <crypto/scatterwalk.h>

#include "ccp-crypto.h"

static inline struct akcipher_request *akcipher_request_cast(
        struct crypto_async_request *req)
{
        return container_of(req, struct akcipher_request, base);
}

static inline int ccp_copy_and_save_keypart(u8 **kpbuf, unsigned int *kplen,
                                            const u8 *buf, size_t sz)
{
        int nskip;

        for (nskip = 0; nskip < sz; nskip++)
                if (buf[nskip])
                        break;
        *kplen = sz - nskip;
        *kpbuf = kmemdup(buf + nskip, *kplen, GFP_KERNEL);
        if (!*kpbuf)
                return -ENOMEM;

        return 0;
}

static int ccp_rsa_complete(struct crypto_async_request *async_req, int ret)
{
        struct akcipher_request *req = akcipher_request_cast(async_req);
        struct ccp_rsa_req_ctx *rctx = akcipher_request_ctx_dma(req);

        if (ret)
                return ret;

        req->dst_len = rctx->cmd.u.rsa.key_size >> 3;

        return 0;
}

static unsigned int ccp_rsa_maxsize(struct crypto_akcipher *tfm)
{
        struct ccp_ctx *ctx = akcipher_tfm_ctx_dma(tfm);

        return ctx->u.rsa.n_len;
}

static int ccp_rsa_crypt(struct akcipher_request *req, bool encrypt)
{
        struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
        struct ccp_ctx *ctx = akcipher_tfm_ctx_dma(tfm);
        struct ccp_rsa_req_ctx *rctx = akcipher_request_ctx_dma(req);
        int ret = 0;

        memset(&rctx->cmd, 0, sizeof(rctx->cmd));
        INIT_LIST_HEAD(&rctx->cmd.entry);
        rctx->cmd.engine = CCP_ENGINE_RSA;

        rctx->cmd.u.rsa.key_size = ctx->u.rsa.key_len; /* in bits */
        if (encrypt) {
                rctx->cmd.u.rsa.exp = &ctx->u.rsa.e_sg;
                rctx->cmd.u.rsa.exp_len = ctx->u.rsa.e_len;
        } else {
                rctx->cmd.u.rsa.exp = &ctx->u.rsa.d_sg;
                rctx->cmd.u.rsa.exp_len = ctx->u.rsa.d_len;
        }
        rctx->cmd.u.rsa.mod = &ctx->u.rsa.n_sg;
        rctx->cmd.u.rsa.mod_len = ctx->u.rsa.n_len;
        rctx->cmd.u.rsa.src = req->src;
        rctx->cmd.u.rsa.src_len = req->src_len;
        rctx->cmd.u.rsa.dst = req->dst;

        ret = ccp_crypto_enqueue_request(&req->base, &rctx->cmd);

        return ret;
}

static int ccp_rsa_encrypt(struct akcipher_request *req)
{
        return ccp_rsa_crypt(req, true);
}

static int ccp_rsa_decrypt(struct akcipher_request *req)
{
        return ccp_rsa_crypt(req, false);
}

static int ccp_check_key_length(unsigned int len)
{
        /* In bits */
        if (len < 8 || len > 4096)
                return -EINVAL;
        return 0;
}

static void ccp_rsa_free_key_bufs(struct ccp_ctx *ctx)
{
        /* Clean up old key data */
        kfree_sensitive(ctx->u.rsa.e_buf);
        ctx->u.rsa.e_buf = NULL;
        ctx->u.rsa.e_len = 0;
        kfree_sensitive(ctx->u.rsa.n_buf);
        ctx->u.rsa.n_buf = NULL;
        ctx->u.rsa.n_len = 0;
        kfree_sensitive(ctx->u.rsa.d_buf);
        ctx->u.rsa.d_buf = NULL;
        ctx->u.rsa.d_len = 0;
}

static int ccp_rsa_setkey(struct crypto_akcipher *tfm, const void *key,
                          unsigned int keylen, bool private)
{
        struct ccp_ctx *ctx = akcipher_tfm_ctx_dma(tfm);
        struct rsa_key raw_key;
        int ret;

        ccp_rsa_free_key_bufs(ctx);
        memset(&raw_key, 0, sizeof(raw_key));

        /* Code borrowed from crypto/rsa.c */
        if (private)
                ret = rsa_parse_priv_key(&raw_key, key, keylen);
        else
                ret = rsa_parse_pub_key(&raw_key, key, keylen);
        if (ret)
                goto n_key;

        ret = ccp_copy_and_save_keypart(&ctx->u.rsa.n_buf, &ctx->u.rsa.n_len,
                                        raw_key.n, raw_key.n_sz);
        if (ret)
                goto key_err;
        sg_init_one(&ctx->u.rsa.n_sg, ctx->u.rsa.n_buf, ctx->u.rsa.n_len);

        ctx->u.rsa.key_len = ctx->u.rsa.n_len << 3; /* convert to bits */
        if (ccp_check_key_length(ctx->u.rsa.key_len)) {
                ret = -EINVAL;
                goto key_err;
        }

        ret = ccp_copy_and_save_keypart(&ctx->u.rsa.e_buf, &ctx->u.rsa.e_len,
                                        raw_key.e, raw_key.e_sz);
        if (ret)
                goto key_err;
        sg_init_one(&ctx->u.rsa.e_sg, ctx->u.rsa.e_buf, ctx->u.rsa.e_len);

        if (private) {
                ret = ccp_copy_and_save_keypart(&ctx->u.rsa.d_buf,
                                                &ctx->u.rsa.d_len,
                                                raw_key.d, raw_key.d_sz);
                if (ret)
                        goto key_err;
                sg_init_one(&ctx->u.rsa.d_sg,
                            ctx->u.rsa.d_buf, ctx->u.rsa.d_len);
        }

        return 0;

key_err:
        ccp_rsa_free_key_bufs(ctx);

n_key:
        return ret;
}

static int ccp_rsa_setprivkey(struct crypto_akcipher *tfm, const void *key,
                              unsigned int keylen)
{
        return ccp_rsa_setkey(tfm, key, keylen, true);
}

static int ccp_rsa_setpubkey(struct crypto_akcipher *tfm, const void *key,
                             unsigned int keylen)
{
        return ccp_rsa_setkey(tfm, key, keylen, false);
}

static int ccp_rsa_init_tfm(struct crypto_akcipher *tfm)
{
        struct ccp_ctx *ctx = akcipher_tfm_ctx_dma(tfm);

        akcipher_set_reqsize_dma(tfm, sizeof(struct ccp_rsa_req_ctx));
        ctx->complete = ccp_rsa_complete;

        return 0;
}

static void ccp_rsa_exit_tfm(struct crypto_akcipher *tfm)
{
        struct ccp_ctx *ctx = akcipher_tfm_ctx_dma(tfm);

        ccp_rsa_free_key_bufs(ctx);
}

static struct akcipher_alg ccp_rsa_defaults = {
        .encrypt = ccp_rsa_encrypt,
        .decrypt = ccp_rsa_decrypt,
        .set_pub_key = ccp_rsa_setpubkey,
        .set_priv_key = ccp_rsa_setprivkey,
        .max_size = ccp_rsa_maxsize,
        .init = ccp_rsa_init_tfm,
        .exit = ccp_rsa_exit_tfm,
        .base = {
                .cra_name = "rsa",
                .cra_driver_name = "rsa-ccp",
                .cra_priority = CCP_CRA_PRIORITY,
                .cra_module = THIS_MODULE,
                .cra_ctxsize = 2 * sizeof(struct ccp_ctx) + CRYPTO_DMA_PADDING,
        },
};

struct ccp_rsa_def {
        unsigned int version;
        const char *name;
        const char *driver_name;
        unsigned int reqsize;
        struct akcipher_alg *alg_defaults;
};

static struct ccp_rsa_def rsa_algs[] = {
        {
                .version        = CCP_VERSION(3, 0),
                .name           = "rsa",
                .driver_name    = "rsa-ccp",
                .reqsize        = sizeof(struct ccp_rsa_req_ctx),
                .alg_defaults   = &ccp_rsa_defaults,
        }
};

static int ccp_register_rsa_alg(struct list_head *head,
                                const struct ccp_rsa_def *def)
{
        struct ccp_crypto_akcipher_alg *ccp_alg;
        struct akcipher_alg *alg;
        int ret;

        ccp_alg = kzalloc_obj(*ccp_alg);
        if (!ccp_alg)
                return -ENOMEM;

        INIT_LIST_HEAD(&ccp_alg->entry);

        alg = &ccp_alg->alg;
        *alg = *def->alg_defaults;
        strscpy(alg->base.cra_name, def->name);
        strscpy(alg->base.cra_driver_name, def->driver_name);
        ret = crypto_register_akcipher(alg);
        if (ret) {
                pr_err("%s akcipher algorithm registration error (%d)\n",
                       alg->base.cra_name, ret);
                kfree(ccp_alg);
                return ret;
        }

        list_add(&ccp_alg->entry, head);

        return 0;
}

int ccp_register_rsa_algs(struct list_head *head)
{
        int i, ret;
        unsigned int ccpversion = ccp_version();

        /* Register the RSA algorithm in standard mode
         * This works for CCP v3 and later
         */
        for (i = 0; i < ARRAY_SIZE(rsa_algs); i++) {
                if (rsa_algs[i].version > ccpversion)
                        continue;
                ret = ccp_register_rsa_alg(head, &rsa_algs[i]);
                if (ret)
                        return ret;
        }

        return 0;
}