mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 01:24:47 +01:00
When all hardware queues are busy and no shareable queue,
new processes fail to apply for queues. To avoid affecting
tasks, support fallback mechanism when hardware queues are
unavailable.
HPRE driver supports DH algorithm, limited to prime numbers up to 4K.
It supports prime numbers larger than 4K via fallback mechanism.
Fixes: 05e7b906aa ("crypto: hisilicon/hpre - add 'ECDH' algorithm")
Signed-off-by: Weili Qian <qianweili@huawei.com>
Signed-off-by: Chenghai Huang <huangchenghai2@huawei.com>
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
1905 lines
44 KiB
C
1905 lines
44 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/* Copyright (c) 2019 HiSilicon Limited. */
|
|
#include <crypto/akcipher.h>
|
|
#include <crypto/dh.h>
|
|
#include <crypto/ecc_curve.h>
|
|
#include <crypto/ecdh.h>
|
|
#include <crypto/rng.h>
|
|
#include <crypto/internal/akcipher.h>
|
|
#include <crypto/internal/kpp.h>
|
|
#include <crypto/internal/rsa.h>
|
|
#include <crypto/kpp.h>
|
|
#include <crypto/scatterwalk.h>
|
|
#include <linux/dma-mapping.h>
|
|
#include <linux/fips.h>
|
|
#include <linux/module.h>
|
|
#include <linux/time.h>
|
|
#include "hpre.h"
|
|
|
|
struct hpre_ctx;
|
|
|
|
#define HPRE_CRYPTO_ALG_PRI 1000
|
|
#define HPRE_ALIGN_SZ 64
|
|
#define HPRE_BITS_2_BYTES_SHIFT 3
|
|
#define HPRE_RSA_512BITS_KSZ 64
|
|
#define HPRE_RSA_1536BITS_KSZ 192
|
|
#define HPRE_CRT_PRMS 5
|
|
#define HPRE_CRT_Q 2
|
|
#define HPRE_CRT_P 3
|
|
#define HPRE_CRT_INV 4
|
|
#define HPRE_DH_G_FLAG 0x02
|
|
#define HPRE_TRY_SEND_TIMES 100
|
|
#define HPRE_INVLD_REQ_ID (-1)
|
|
|
|
#define HPRE_SQE_ALG_BITS 5
|
|
#define HPRE_SQE_DONE_SHIFT 30
|
|
#define HPRE_DH_MAX_P_SZ 512
|
|
|
|
#define HPRE_DFX_SEC_TO_US 1000000
|
|
#define HPRE_DFX_US_TO_NS 1000
|
|
|
|
#define HPRE_ENABLE_HPCORE_SHIFT 7
|
|
|
|
/* due to nist p521 */
|
|
#define HPRE_ECC_MAX_KSZ 66
|
|
|
|
/* size in bytes of the n prime */
|
|
#define HPRE_ECC_NIST_P192_N_SIZE 24
|
|
#define HPRE_ECC_NIST_P256_N_SIZE 32
|
|
#define HPRE_ECC_NIST_P384_N_SIZE 48
|
|
|
|
/* size in bytes */
|
|
#define HPRE_ECC_HW256_KSZ_B 32
|
|
#define HPRE_ECC_HW384_KSZ_B 48
|
|
|
|
/* capability register mask of driver */
|
|
#define HPRE_DRV_RSA_MASK_CAP BIT(0)
|
|
#define HPRE_DRV_DH_MASK_CAP BIT(1)
|
|
#define HPRE_DRV_ECDH_MASK_CAP BIT(2)
|
|
#define HPRE_DRV_X25519_MASK_CAP BIT(5)
|
|
|
|
static DEFINE_MUTEX(hpre_algs_lock);
|
|
static unsigned int hpre_available_devs;
|
|
|
|
typedef void (*hpre_cb)(struct hpre_ctx *ctx, void *sqe);
|
|
|
|
struct hpre_rsa_ctx {
|
|
/* low address: e--->n */
|
|
char *pubkey;
|
|
dma_addr_t dma_pubkey;
|
|
|
|
/* low address: d--->n */
|
|
char *prikey;
|
|
dma_addr_t dma_prikey;
|
|
|
|
/* low address: dq->dp->q->p->qinv */
|
|
char *crt_prikey;
|
|
dma_addr_t dma_crt_prikey;
|
|
|
|
struct crypto_akcipher *soft_tfm;
|
|
};
|
|
|
|
struct hpre_dh_ctx {
|
|
/*
|
|
* If base is g we compute the public key
|
|
* ya = g^xa mod p; [RFC2631 sec 2.1.1]
|
|
* else if base if the counterpart public key we
|
|
* compute the shared secret
|
|
* ZZ = yb^xa mod p; [RFC2631 sec 2.1.1]
|
|
* low address: d--->n, please refer to Hisilicon HPRE UM
|
|
*/
|
|
char *xa_p;
|
|
dma_addr_t dma_xa_p;
|
|
|
|
char *g; /* m */
|
|
dma_addr_t dma_g;
|
|
struct crypto_kpp *soft_tfm;
|
|
};
|
|
|
|
struct hpre_ecdh_ctx {
|
|
/* low address: p->a->k->b */
|
|
unsigned char *p;
|
|
dma_addr_t dma_p;
|
|
|
|
/* low address: x->y */
|
|
unsigned char *g;
|
|
dma_addr_t dma_g;
|
|
struct crypto_kpp *soft_tfm;
|
|
};
|
|
|
|
struct hpre_ctx {
|
|
struct hisi_qp *qp;
|
|
struct device *dev;
|
|
struct hpre *hpre;
|
|
unsigned int key_sz;
|
|
bool crt_g2_mode;
|
|
union {
|
|
struct hpre_rsa_ctx rsa;
|
|
struct hpre_dh_ctx dh;
|
|
struct hpre_ecdh_ctx ecdh;
|
|
};
|
|
/* for ecc algorithms */
|
|
unsigned int curve_id;
|
|
/* for high performance core */
|
|
u8 enable_hpcore;
|
|
bool fallback;
|
|
};
|
|
|
|
struct hpre_asym_request {
|
|
char *src;
|
|
char *dst;
|
|
struct hpre_sqe req;
|
|
struct hpre_ctx *ctx;
|
|
union {
|
|
struct akcipher_request *rsa;
|
|
struct kpp_request *dh;
|
|
struct kpp_request *ecdh;
|
|
} areq;
|
|
int err;
|
|
hpre_cb cb;
|
|
struct timespec64 req_time;
|
|
};
|
|
|
|
static inline unsigned int hpre_align_sz(void)
|
|
{
|
|
return ((crypto_dma_align() - 1) | (HPRE_ALIGN_SZ - 1)) + 1;
|
|
}
|
|
|
|
static inline unsigned int hpre_align_pd(void)
|
|
{
|
|
return (hpre_align_sz() - 1) & ~(crypto_tfm_ctx_alignment() - 1);
|
|
}
|
|
|
|
static void hpre_dfx_add_req_time(struct hpre_asym_request *hpre_req)
|
|
{
|
|
struct hpre_ctx *ctx = hpre_req->ctx;
|
|
struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
|
|
|
|
if (atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value))
|
|
ktime_get_ts64(&hpre_req->req_time);
|
|
}
|
|
|
|
static int hpre_get_data_dma_addr(struct hpre_asym_request *hpre_req,
|
|
struct scatterlist *data, unsigned int len,
|
|
int is_src, dma_addr_t *tmp)
|
|
{
|
|
struct device *dev = hpre_req->ctx->dev;
|
|
enum dma_data_direction dma_dir;
|
|
|
|
if (is_src) {
|
|
hpre_req->src = NULL;
|
|
dma_dir = DMA_TO_DEVICE;
|
|
} else {
|
|
hpre_req->dst = NULL;
|
|
dma_dir = DMA_FROM_DEVICE;
|
|
}
|
|
*tmp = dma_map_single(dev, sg_virt(data), len, dma_dir);
|
|
if (unlikely(dma_mapping_error(dev, *tmp))) {
|
|
dev_err(dev, "dma map data err!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_prepare_dma_buf(struct hpre_asym_request *hpre_req,
|
|
struct scatterlist *data, unsigned int len,
|
|
int is_src, dma_addr_t *tmp)
|
|
{
|
|
struct hpre_ctx *ctx = hpre_req->ctx;
|
|
struct device *dev = ctx->dev;
|
|
void *ptr;
|
|
int shift;
|
|
|
|
shift = ctx->key_sz - len;
|
|
if (unlikely(shift < 0))
|
|
return -EINVAL;
|
|
|
|
ptr = dma_alloc_coherent(dev, ctx->key_sz, tmp, GFP_ATOMIC);
|
|
if (unlikely(!ptr))
|
|
return -ENOMEM;
|
|
|
|
if (is_src) {
|
|
scatterwalk_map_and_copy(ptr + shift, data, 0, len, 0);
|
|
hpre_req->src = ptr;
|
|
} else {
|
|
hpre_req->dst = ptr;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_hw_data_init(struct hpre_asym_request *hpre_req,
|
|
struct scatterlist *data, unsigned int len,
|
|
int is_src, int is_dh)
|
|
{
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
struct hpre_ctx *ctx = hpre_req->ctx;
|
|
dma_addr_t tmp = 0;
|
|
int ret;
|
|
|
|
/* when the data is dh's source, we should format it */
|
|
if ((sg_is_last(data) && len == ctx->key_sz) &&
|
|
((is_dh && !is_src) || !is_dh))
|
|
ret = hpre_get_data_dma_addr(hpre_req, data, len, is_src, &tmp);
|
|
else
|
|
ret = hpre_prepare_dma_buf(hpre_req, data, len, is_src, &tmp);
|
|
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (is_src)
|
|
msg->in = cpu_to_le64(tmp);
|
|
else
|
|
msg->out = cpu_to_le64(tmp);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hpre_hw_data_clr_all(struct hpre_ctx *ctx,
|
|
struct hpre_asym_request *req,
|
|
struct scatterlist *dst,
|
|
struct scatterlist *src)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
struct hpre_sqe *sqe = &req->req;
|
|
dma_addr_t tmp;
|
|
|
|
tmp = le64_to_cpu(sqe->in);
|
|
if (unlikely(dma_mapping_error(dev, tmp)))
|
|
return;
|
|
|
|
if (src) {
|
|
if (req->src)
|
|
dma_free_coherent(dev, ctx->key_sz, req->src, tmp);
|
|
else
|
|
dma_unmap_single(dev, tmp, ctx->key_sz, DMA_TO_DEVICE);
|
|
}
|
|
|
|
tmp = le64_to_cpu(sqe->out);
|
|
if (unlikely(dma_mapping_error(dev, tmp)))
|
|
return;
|
|
|
|
if (req->dst) {
|
|
if (dst)
|
|
scatterwalk_map_and_copy(req->dst, dst, 0,
|
|
ctx->key_sz, 1);
|
|
dma_free_coherent(dev, ctx->key_sz, req->dst, tmp);
|
|
} else {
|
|
dma_unmap_single(dev, tmp, ctx->key_sz, DMA_FROM_DEVICE);
|
|
}
|
|
}
|
|
|
|
static int hpre_alg_res_post_hf(struct hpre_ctx *ctx, struct hpre_sqe *sqe,
|
|
void **kreq)
|
|
{
|
|
unsigned int err, done, alg;
|
|
|
|
#define HPRE_NO_HW_ERR 0
|
|
#define HPRE_HW_TASK_DONE 3
|
|
#define HREE_HW_ERR_MASK GENMASK(10, 0)
|
|
#define HREE_SQE_DONE_MASK GENMASK(1, 0)
|
|
#define HREE_ALG_TYPE_MASK GENMASK(4, 0)
|
|
*kreq = (void *)le64_to_cpu(sqe->tag);
|
|
|
|
err = (le32_to_cpu(sqe->dw0) >> HPRE_SQE_ALG_BITS) &
|
|
HREE_HW_ERR_MASK;
|
|
done = (le32_to_cpu(sqe->dw0) >> HPRE_SQE_DONE_SHIFT) &
|
|
HREE_SQE_DONE_MASK;
|
|
if (likely(err == HPRE_NO_HW_ERR && done == HPRE_HW_TASK_DONE))
|
|
return 0;
|
|
|
|
alg = le32_to_cpu(sqe->dw0) & HREE_ALG_TYPE_MASK;
|
|
dev_err_ratelimited(ctx->dev, "alg[0x%x] error: done[0x%x], etype[0x%x]\n",
|
|
alg, done, err);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void hpre_ctx_clear(struct hpre_ctx *ctx, bool is_clear_all)
|
|
{
|
|
if (is_clear_all)
|
|
hisi_qm_free_qps(&ctx->qp, 1);
|
|
|
|
ctx->crt_g2_mode = false;
|
|
ctx->key_sz = 0;
|
|
}
|
|
|
|
static bool hpre_is_bd_timeout(struct hpre_asym_request *req,
|
|
u64 overtime_thrhld)
|
|
{
|
|
struct timespec64 reply_time;
|
|
u64 time_use_us;
|
|
|
|
ktime_get_ts64(&reply_time);
|
|
time_use_us = (reply_time.tv_sec - req->req_time.tv_sec) *
|
|
HPRE_DFX_SEC_TO_US +
|
|
(reply_time.tv_nsec - req->req_time.tv_nsec) /
|
|
HPRE_DFX_US_TO_NS;
|
|
|
|
if (time_use_us <= overtime_thrhld)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void hpre_dh_cb(struct hpre_ctx *ctx, void *resp)
|
|
{
|
|
struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
|
|
struct hpre_asym_request *req;
|
|
struct kpp_request *areq;
|
|
u64 overtime_thrhld;
|
|
int ret;
|
|
|
|
ret = hpre_alg_res_post_hf(ctx, resp, (void **)&req);
|
|
areq = req->areq.dh;
|
|
areq->dst_len = ctx->key_sz;
|
|
|
|
overtime_thrhld = atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value);
|
|
if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld))
|
|
atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value);
|
|
|
|
hpre_hw_data_clr_all(ctx, req, areq->dst, areq->src);
|
|
kpp_request_complete(areq, ret);
|
|
atomic64_inc(&dfx[HPRE_RECV_CNT].value);
|
|
}
|
|
|
|
static void hpre_rsa_cb(struct hpre_ctx *ctx, void *resp)
|
|
{
|
|
struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
|
|
struct hpre_asym_request *req;
|
|
struct akcipher_request *areq;
|
|
u64 overtime_thrhld;
|
|
int ret;
|
|
|
|
ret = hpre_alg_res_post_hf(ctx, resp, (void **)&req);
|
|
|
|
overtime_thrhld = atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value);
|
|
if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld))
|
|
atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value);
|
|
|
|
areq = req->areq.rsa;
|
|
areq->dst_len = ctx->key_sz;
|
|
hpre_hw_data_clr_all(ctx, req, areq->dst, areq->src);
|
|
akcipher_request_complete(areq, ret);
|
|
atomic64_inc(&dfx[HPRE_RECV_CNT].value);
|
|
}
|
|
|
|
static void hpre_alg_cb(struct hisi_qp *qp, void *resp)
|
|
{
|
|
struct hpre_asym_request *h_req;
|
|
struct hpre_sqe *sqe = resp;
|
|
|
|
h_req = (struct hpre_asym_request *)le64_to_cpu(sqe->tag);
|
|
if (unlikely(!h_req)) {
|
|
pr_err("Failed to get request, and qp_id is %u\n", qp->qp_id);
|
|
return;
|
|
}
|
|
|
|
h_req->cb(h_req->ctx, resp);
|
|
}
|
|
|
|
static int hpre_ctx_init(struct hpre_ctx *ctx, u8 type)
|
|
{
|
|
struct hisi_qp *qp;
|
|
struct hpre *hpre;
|
|
|
|
qp = hpre_create_qp(type);
|
|
if (!qp) {
|
|
ctx->qp = NULL;
|
|
return -ENODEV;
|
|
}
|
|
|
|
qp->req_cb = hpre_alg_cb;
|
|
ctx->qp = qp;
|
|
ctx->dev = &qp->qm->pdev->dev;
|
|
hpre = container_of(ctx->qp->qm, struct hpre, qm);
|
|
ctx->hpre = hpre;
|
|
ctx->key_sz = 0;
|
|
ctx->crt_g2_mode = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_msg_request_set(struct hpre_ctx *ctx, void *req, bool is_rsa)
|
|
{
|
|
struct hpre_asym_request *h_req;
|
|
struct hpre_sqe *msg;
|
|
void *tmp;
|
|
|
|
if (is_rsa) {
|
|
struct akcipher_request *akreq = req;
|
|
|
|
if (akreq->dst_len < ctx->key_sz) {
|
|
akreq->dst_len = ctx->key_sz;
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
tmp = akcipher_request_ctx(akreq);
|
|
h_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
h_req->cb = hpre_rsa_cb;
|
|
h_req->areq.rsa = akreq;
|
|
msg = &h_req->req;
|
|
memset(msg, 0, sizeof(*msg));
|
|
} else {
|
|
struct kpp_request *kreq = req;
|
|
|
|
if (kreq->dst_len < ctx->key_sz) {
|
|
kreq->dst_len = ctx->key_sz;
|
|
return -EOVERFLOW;
|
|
}
|
|
|
|
tmp = kpp_request_ctx(kreq);
|
|
h_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
h_req->cb = hpre_dh_cb;
|
|
h_req->areq.dh = kreq;
|
|
msg = &h_req->req;
|
|
memset(msg, 0, sizeof(*msg));
|
|
msg->key = cpu_to_le64(ctx->dh.dma_xa_p);
|
|
}
|
|
|
|
msg->in = cpu_to_le64(DMA_MAPPING_ERROR);
|
|
msg->out = cpu_to_le64(DMA_MAPPING_ERROR);
|
|
msg->dw0 |= cpu_to_le32(0x1 << HPRE_SQE_DONE_SHIFT);
|
|
msg->task_len1 = (ctx->key_sz >> HPRE_BITS_2_BYTES_SHIFT) - 1;
|
|
h_req->ctx = ctx;
|
|
|
|
hpre_dfx_add_req_time(h_req);
|
|
msg->tag = cpu_to_le64((uintptr_t)h_req);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_send(struct hpre_ctx *ctx, struct hpre_sqe *msg)
|
|
{
|
|
struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
|
|
int ctr = 0;
|
|
int ret;
|
|
|
|
do {
|
|
atomic64_inc(&dfx[HPRE_SEND_CNT].value);
|
|
ret = hisi_qp_send(ctx->qp, msg);
|
|
if (ret != -EBUSY)
|
|
break;
|
|
atomic64_inc(&dfx[HPRE_SEND_BUSY_CNT].value);
|
|
} while (ctr++ < HPRE_TRY_SEND_TIMES);
|
|
|
|
if (likely(!ret))
|
|
return ret;
|
|
|
|
if (ret != -EBUSY)
|
|
atomic64_inc(&dfx[HPRE_SEND_FAIL_CNT].value);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_dh_compute_value(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
void *tmp = kpp_request_ctx(req);
|
|
struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
int ret;
|
|
|
|
ret = hpre_msg_request_set(ctx, req, false);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (req->src) {
|
|
ret = hpre_hw_data_init(hpre_req, req->src, req->src_len, 1, 1);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
} else {
|
|
msg->in = cpu_to_le64(ctx->dh.dma_g);
|
|
}
|
|
|
|
ret = hpre_hw_data_init(hpre_req, req->dst, req->dst_len, 0, 1);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
|
|
if (ctx->crt_g2_mode && !req->src)
|
|
msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) | HPRE_ALG_DH_G2);
|
|
else
|
|
msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) | HPRE_ALG_DH);
|
|
|
|
/* success */
|
|
ret = hpre_send(ctx, msg);
|
|
if (likely(!ret))
|
|
return -EINPROGRESS;
|
|
|
|
clear_all:
|
|
hpre_hw_data_clr_all(ctx, hpre_req, req->dst, req->src);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static struct kpp_request *hpre_dh_prepare_fb_req(struct kpp_request *req)
|
|
{
|
|
struct kpp_request *fb_req = kpp_request_ctx(req);
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
kpp_request_set_tfm(fb_req, ctx->dh.soft_tfm);
|
|
kpp_request_set_callback(fb_req, req->base.flags, req->base.complete, req->base.data);
|
|
kpp_request_set_input(fb_req, req->src, req->src_len);
|
|
kpp_request_set_output(fb_req, req->dst, req->dst_len);
|
|
|
|
return fb_req;
|
|
}
|
|
|
|
static int hpre_dh_generate_public_key(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
struct kpp_request *fb_req;
|
|
|
|
if (ctx->fallback) {
|
|
fb_req = hpre_dh_prepare_fb_req(req);
|
|
return crypto_kpp_generate_public_key(fb_req);
|
|
}
|
|
|
|
return hpre_dh_compute_value(req);
|
|
}
|
|
|
|
static int hpre_dh_compute_shared_secret(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
struct kpp_request *fb_req;
|
|
|
|
if (ctx->fallback) {
|
|
fb_req = hpre_dh_prepare_fb_req(req);
|
|
return crypto_kpp_compute_shared_secret(fb_req);
|
|
}
|
|
|
|
return hpre_dh_compute_value(req);
|
|
}
|
|
|
|
static int hpre_is_dh_params_length_valid(unsigned int key_sz)
|
|
{
|
|
#define _HPRE_DH_GRP1 768
|
|
#define _HPRE_DH_GRP2 1024
|
|
#define _HPRE_DH_GRP5 1536
|
|
#define _HPRE_DH_GRP14 2048
|
|
#define _HPRE_DH_GRP15 3072
|
|
#define _HPRE_DH_GRP16 4096
|
|
switch (key_sz) {
|
|
case _HPRE_DH_GRP1:
|
|
case _HPRE_DH_GRP2:
|
|
case _HPRE_DH_GRP5:
|
|
case _HPRE_DH_GRP14:
|
|
case _HPRE_DH_GRP15:
|
|
case _HPRE_DH_GRP16:
|
|
return 0;
|
|
default:
|
|
return -EINVAL;
|
|
}
|
|
}
|
|
|
|
static int hpre_dh_set_params(struct hpre_ctx *ctx, struct dh *params)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
unsigned int sz;
|
|
|
|
sz = ctx->key_sz = params->p_size;
|
|
ctx->dh.xa_p = dma_alloc_coherent(dev, sz << 1,
|
|
&ctx->dh.dma_xa_p, GFP_KERNEL);
|
|
if (!ctx->dh.xa_p)
|
|
return -ENOMEM;
|
|
|
|
memcpy(ctx->dh.xa_p + sz, params->p, sz);
|
|
|
|
/* If g equals 2 don't copy it */
|
|
if (params->g_size == 1 && *(char *)params->g == HPRE_DH_G_FLAG) {
|
|
ctx->crt_g2_mode = true;
|
|
return 0;
|
|
}
|
|
|
|
ctx->dh.g = dma_alloc_coherent(dev, sz, &ctx->dh.dma_g, GFP_KERNEL);
|
|
if (!ctx->dh.g) {
|
|
dma_free_coherent(dev, sz << 1, ctx->dh.xa_p,
|
|
ctx->dh.dma_xa_p);
|
|
ctx->dh.xa_p = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memcpy(ctx->dh.g + (sz - params->g_size), params->g, params->g_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hpre_dh_clear_ctx(struct hpre_ctx *ctx, bool is_clear_all)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
unsigned int sz = ctx->key_sz;
|
|
|
|
if (!ctx->qp)
|
|
return;
|
|
|
|
if (ctx->dh.g) {
|
|
dma_free_coherent(dev, sz, ctx->dh.g, ctx->dh.dma_g);
|
|
ctx->dh.g = NULL;
|
|
}
|
|
|
|
if (ctx->dh.xa_p) {
|
|
memzero_explicit(ctx->dh.xa_p, sz);
|
|
dma_free_coherent(dev, sz << 1, ctx->dh.xa_p,
|
|
ctx->dh.dma_xa_p);
|
|
ctx->dh.xa_p = NULL;
|
|
}
|
|
|
|
hpre_ctx_clear(ctx, is_clear_all);
|
|
}
|
|
|
|
static int hpre_dh_set_secret(struct crypto_kpp *tfm, const void *buf,
|
|
unsigned int len)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
struct dh params;
|
|
int ret;
|
|
|
|
if (crypto_dh_decode_key(buf, len, ¶ms) < 0)
|
|
return -EINVAL;
|
|
|
|
if (!ctx->qp)
|
|
goto set_soft_secret;
|
|
|
|
if (hpre_is_dh_params_length_valid(params.p_size <<
|
|
HPRE_BITS_2_BYTES_SHIFT))
|
|
goto set_soft_secret;
|
|
|
|
/* Free old secret if any */
|
|
hpre_dh_clear_ctx(ctx, false);
|
|
|
|
ret = hpre_dh_set_params(ctx, ¶ms);
|
|
if (ret < 0)
|
|
goto err_clear_ctx;
|
|
|
|
memcpy(ctx->dh.xa_p + (ctx->key_sz - params.key_size), params.key,
|
|
params.key_size);
|
|
|
|
ctx->fallback = false;
|
|
return 0;
|
|
|
|
err_clear_ctx:
|
|
hpre_dh_clear_ctx(ctx, false);
|
|
return ret;
|
|
set_soft_secret:
|
|
ctx->fallback = true;
|
|
return crypto_kpp_set_secret(ctx->dh.soft_tfm, buf, len);
|
|
}
|
|
|
|
static unsigned int hpre_dh_max_size(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
if (ctx->fallback)
|
|
return crypto_kpp_maxsize(ctx->dh.soft_tfm);
|
|
|
|
return ctx->key_sz;
|
|
}
|
|
|
|
static int hpre_dh_init_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
const char *alg = kpp_alg_name(tfm);
|
|
unsigned int reqsize;
|
|
int ret;
|
|
|
|
ctx->dh.soft_tfm = crypto_alloc_kpp(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
|
|
if (IS_ERR(ctx->dh.soft_tfm)) {
|
|
pr_err("Failed to alloc dh tfm!\n");
|
|
return PTR_ERR(ctx->dh.soft_tfm);
|
|
}
|
|
|
|
crypto_kpp_set_flags(ctx->dh.soft_tfm, crypto_kpp_get_flags(tfm));
|
|
|
|
reqsize = max(sizeof(struct hpre_asym_request) + hpre_align_pd(),
|
|
sizeof(struct kpp_request) + crypto_kpp_reqsize(ctx->dh.soft_tfm));
|
|
kpp_set_reqsize(tfm, reqsize);
|
|
|
|
ret = hpre_ctx_init(ctx, HPRE_V2_ALG_TYPE);
|
|
if (ret && ret != -ENODEV) {
|
|
crypto_free_kpp(ctx->dh.soft_tfm);
|
|
return ret;
|
|
} else if (ret == -ENODEV) {
|
|
ctx->fallback = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hpre_dh_exit_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
hpre_dh_clear_ctx(ctx, true);
|
|
crypto_free_kpp(ctx->dh.soft_tfm);
|
|
}
|
|
|
|
static void hpre_rsa_drop_leading_zeros(const char **ptr, size_t *len)
|
|
{
|
|
while (!**ptr && *len) {
|
|
(*ptr)++;
|
|
(*len)--;
|
|
}
|
|
}
|
|
|
|
static bool hpre_rsa_key_size_is_support(unsigned int len)
|
|
{
|
|
unsigned int bits = len << HPRE_BITS_2_BYTES_SHIFT;
|
|
|
|
#define _RSA_1024BITS_KEY_WDTH 1024
|
|
#define _RSA_2048BITS_KEY_WDTH 2048
|
|
#define _RSA_3072BITS_KEY_WDTH 3072
|
|
#define _RSA_4096BITS_KEY_WDTH 4096
|
|
|
|
switch (bits) {
|
|
case _RSA_1024BITS_KEY_WDTH:
|
|
case _RSA_2048BITS_KEY_WDTH:
|
|
case _RSA_3072BITS_KEY_WDTH:
|
|
case _RSA_4096BITS_KEY_WDTH:
|
|
return true;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
static int hpre_rsa_enc(struct akcipher_request *req)
|
|
{
|
|
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
void *tmp = akcipher_request_ctx(req);
|
|
struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
int ret;
|
|
|
|
/* For unsupported key size and unavailable devices, use soft tfm instead */
|
|
if (ctx->fallback) {
|
|
akcipher_request_set_tfm(req, ctx->rsa.soft_tfm);
|
|
ret = crypto_akcipher_encrypt(req);
|
|
akcipher_request_set_tfm(req, tfm);
|
|
return ret;
|
|
}
|
|
|
|
if (unlikely(!ctx->rsa.pubkey))
|
|
return -EINVAL;
|
|
|
|
ret = hpre_msg_request_set(ctx, req, true);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
msg->dw0 |= cpu_to_le32(HPRE_ALG_NC_NCRT);
|
|
msg->key = cpu_to_le64(ctx->rsa.dma_pubkey);
|
|
|
|
ret = hpre_hw_data_init(hpre_req, req->src, req->src_len, 1, 0);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
|
|
ret = hpre_hw_data_init(hpre_req, req->dst, req->dst_len, 0, 0);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
|
|
/* success */
|
|
ret = hpre_send(ctx, msg);
|
|
if (likely(!ret))
|
|
return -EINPROGRESS;
|
|
|
|
clear_all:
|
|
hpre_hw_data_clr_all(ctx, hpre_req, req->dst, req->src);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_rsa_dec(struct akcipher_request *req)
|
|
{
|
|
struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
void *tmp = akcipher_request_ctx(req);
|
|
struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
int ret;
|
|
|
|
/* For unsupported key size and unavailable devices, use soft tfm instead */
|
|
if (ctx->fallback) {
|
|
akcipher_request_set_tfm(req, ctx->rsa.soft_tfm);
|
|
ret = crypto_akcipher_decrypt(req);
|
|
akcipher_request_set_tfm(req, tfm);
|
|
return ret;
|
|
}
|
|
|
|
if (unlikely(!ctx->rsa.prikey))
|
|
return -EINVAL;
|
|
|
|
ret = hpre_msg_request_set(ctx, req, true);
|
|
if (unlikely(ret))
|
|
return ret;
|
|
|
|
if (ctx->crt_g2_mode) {
|
|
msg->key = cpu_to_le64(ctx->rsa.dma_crt_prikey);
|
|
msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) |
|
|
HPRE_ALG_NC_CRT);
|
|
} else {
|
|
msg->key = cpu_to_le64(ctx->rsa.dma_prikey);
|
|
msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) |
|
|
HPRE_ALG_NC_NCRT);
|
|
}
|
|
|
|
ret = hpre_hw_data_init(hpre_req, req->src, req->src_len, 1, 0);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
|
|
ret = hpre_hw_data_init(hpre_req, req->dst, req->dst_len, 0, 0);
|
|
if (unlikely(ret))
|
|
goto clear_all;
|
|
|
|
/* success */
|
|
ret = hpre_send(ctx, msg);
|
|
if (likely(!ret))
|
|
return -EINPROGRESS;
|
|
|
|
clear_all:
|
|
hpre_hw_data_clr_all(ctx, hpre_req, req->dst, req->src);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_rsa_set_n(struct hpre_ctx *ctx, const char *value,
|
|
size_t vlen, bool private)
|
|
{
|
|
const char *ptr = value;
|
|
|
|
hpre_rsa_drop_leading_zeros(&ptr, &vlen);
|
|
|
|
ctx->key_sz = vlen;
|
|
|
|
/* if invalid key size provided, we use software tfm */
|
|
if (!hpre_rsa_key_size_is_support(ctx->key_sz)) {
|
|
ctx->fallback = true;
|
|
return 0;
|
|
}
|
|
|
|
ctx->rsa.pubkey = dma_alloc_coherent(ctx->dev, vlen << 1,
|
|
&ctx->rsa.dma_pubkey,
|
|
GFP_KERNEL);
|
|
if (!ctx->rsa.pubkey)
|
|
return -ENOMEM;
|
|
|
|
if (private) {
|
|
ctx->rsa.prikey = dma_alloc_coherent(ctx->dev, vlen << 1,
|
|
&ctx->rsa.dma_prikey,
|
|
GFP_KERNEL);
|
|
if (!ctx->rsa.prikey) {
|
|
dma_free_coherent(ctx->dev, vlen << 1,
|
|
ctx->rsa.pubkey,
|
|
ctx->rsa.dma_pubkey);
|
|
ctx->rsa.pubkey = NULL;
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(ctx->rsa.prikey + vlen, ptr, vlen);
|
|
}
|
|
memcpy(ctx->rsa.pubkey + vlen, ptr, vlen);
|
|
|
|
/* Using hardware HPRE to do RSA */
|
|
return 1;
|
|
}
|
|
|
|
static int hpre_rsa_set_e(struct hpre_ctx *ctx, const char *value,
|
|
size_t vlen)
|
|
{
|
|
const char *ptr = value;
|
|
|
|
hpre_rsa_drop_leading_zeros(&ptr, &vlen);
|
|
|
|
if (!ctx->key_sz || !vlen || vlen > ctx->key_sz)
|
|
return -EINVAL;
|
|
|
|
memcpy(ctx->rsa.pubkey + ctx->key_sz - vlen, ptr, vlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_rsa_set_d(struct hpre_ctx *ctx, const char *value,
|
|
size_t vlen)
|
|
{
|
|
const char *ptr = value;
|
|
|
|
hpre_rsa_drop_leading_zeros(&ptr, &vlen);
|
|
|
|
if (!ctx->key_sz || !vlen || vlen > ctx->key_sz)
|
|
return -EINVAL;
|
|
|
|
memcpy(ctx->rsa.prikey + ctx->key_sz - vlen, ptr, vlen);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_crt_para_get(char *para, size_t para_sz,
|
|
const char *raw, size_t raw_sz)
|
|
{
|
|
const char *ptr = raw;
|
|
size_t len = raw_sz;
|
|
|
|
hpre_rsa_drop_leading_zeros(&ptr, &len);
|
|
if (!len || len > para_sz)
|
|
return -EINVAL;
|
|
|
|
memcpy(para + para_sz - len, ptr, len);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_rsa_setkey_crt(struct hpre_ctx *ctx, struct rsa_key *rsa_key)
|
|
{
|
|
unsigned int hlf_ksz = ctx->key_sz >> 1;
|
|
struct device *dev = ctx->dev;
|
|
u64 offset;
|
|
int ret;
|
|
|
|
ctx->rsa.crt_prikey = dma_alloc_coherent(dev, hlf_ksz * HPRE_CRT_PRMS,
|
|
&ctx->rsa.dma_crt_prikey,
|
|
GFP_KERNEL);
|
|
if (!ctx->rsa.crt_prikey)
|
|
return -ENOMEM;
|
|
|
|
ret = hpre_crt_para_get(ctx->rsa.crt_prikey, hlf_ksz,
|
|
rsa_key->dq, rsa_key->dq_sz);
|
|
if (ret)
|
|
goto free_key;
|
|
|
|
offset = hlf_ksz;
|
|
ret = hpre_crt_para_get(ctx->rsa.crt_prikey + offset, hlf_ksz,
|
|
rsa_key->dp, rsa_key->dp_sz);
|
|
if (ret)
|
|
goto free_key;
|
|
|
|
offset = hlf_ksz * HPRE_CRT_Q;
|
|
ret = hpre_crt_para_get(ctx->rsa.crt_prikey + offset, hlf_ksz,
|
|
rsa_key->q, rsa_key->q_sz);
|
|
if (ret)
|
|
goto free_key;
|
|
|
|
offset = hlf_ksz * HPRE_CRT_P;
|
|
ret = hpre_crt_para_get(ctx->rsa.crt_prikey + offset, hlf_ksz,
|
|
rsa_key->p, rsa_key->p_sz);
|
|
if (ret)
|
|
goto free_key;
|
|
|
|
offset = hlf_ksz * HPRE_CRT_INV;
|
|
ret = hpre_crt_para_get(ctx->rsa.crt_prikey + offset, hlf_ksz,
|
|
rsa_key->qinv, rsa_key->qinv_sz);
|
|
if (ret)
|
|
goto free_key;
|
|
|
|
ctx->crt_g2_mode = true;
|
|
|
|
return 0;
|
|
|
|
free_key:
|
|
offset = hlf_ksz * HPRE_CRT_PRMS;
|
|
memzero_explicit(ctx->rsa.crt_prikey, offset);
|
|
dma_free_coherent(dev, hlf_ksz * HPRE_CRT_PRMS, ctx->rsa.crt_prikey,
|
|
ctx->rsa.dma_crt_prikey);
|
|
ctx->rsa.crt_prikey = NULL;
|
|
ctx->crt_g2_mode = false;
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* If it is clear all, all the resources of the QP will be cleaned. */
|
|
static void hpre_rsa_clear_ctx(struct hpre_ctx *ctx, bool is_clear_all)
|
|
{
|
|
unsigned int half_key_sz = ctx->key_sz >> 1;
|
|
struct device *dev = ctx->dev;
|
|
|
|
if (!ctx->qp)
|
|
return;
|
|
|
|
if (ctx->rsa.pubkey) {
|
|
dma_free_coherent(dev, ctx->key_sz << 1,
|
|
ctx->rsa.pubkey, ctx->rsa.dma_pubkey);
|
|
ctx->rsa.pubkey = NULL;
|
|
}
|
|
|
|
if (ctx->rsa.crt_prikey) {
|
|
memzero_explicit(ctx->rsa.crt_prikey,
|
|
half_key_sz * HPRE_CRT_PRMS);
|
|
dma_free_coherent(dev, half_key_sz * HPRE_CRT_PRMS,
|
|
ctx->rsa.crt_prikey, ctx->rsa.dma_crt_prikey);
|
|
ctx->rsa.crt_prikey = NULL;
|
|
}
|
|
|
|
if (ctx->rsa.prikey) {
|
|
memzero_explicit(ctx->rsa.prikey, ctx->key_sz);
|
|
dma_free_coherent(dev, ctx->key_sz << 1, ctx->rsa.prikey,
|
|
ctx->rsa.dma_prikey);
|
|
ctx->rsa.prikey = NULL;
|
|
}
|
|
|
|
hpre_ctx_clear(ctx, is_clear_all);
|
|
}
|
|
|
|
/*
|
|
* we should judge if it is CRT or not,
|
|
* CRT: return true, N-CRT: return false .
|
|
*/
|
|
static bool hpre_is_crt_key(struct rsa_key *key)
|
|
{
|
|
u16 len = key->p_sz + key->q_sz + key->dp_sz + key->dq_sz +
|
|
key->qinv_sz;
|
|
|
|
#define LEN_OF_NCRT_PARA 5
|
|
|
|
/* N-CRT less than 5 parameters */
|
|
return len > LEN_OF_NCRT_PARA;
|
|
}
|
|
|
|
static int hpre_rsa_setkey(struct hpre_ctx *ctx, const void *key,
|
|
unsigned int keylen, bool private)
|
|
{
|
|
struct rsa_key rsa_key;
|
|
int ret;
|
|
|
|
hpre_rsa_clear_ctx(ctx, false);
|
|
|
|
if (private)
|
|
ret = rsa_parse_priv_key(&rsa_key, key, keylen);
|
|
else
|
|
ret = rsa_parse_pub_key(&rsa_key, key, keylen);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
ret = hpre_rsa_set_n(ctx, rsa_key.n, rsa_key.n_sz, private);
|
|
if (ret <= 0)
|
|
return ret;
|
|
|
|
if (private) {
|
|
ret = hpre_rsa_set_d(ctx, rsa_key.d, rsa_key.d_sz);
|
|
if (ret < 0)
|
|
goto free;
|
|
|
|
if (hpre_is_crt_key(&rsa_key)) {
|
|
ret = hpre_rsa_setkey_crt(ctx, &rsa_key);
|
|
if (ret < 0)
|
|
goto free;
|
|
}
|
|
}
|
|
|
|
ret = hpre_rsa_set_e(ctx, rsa_key.e, rsa_key.e_sz);
|
|
if (ret < 0)
|
|
goto free;
|
|
|
|
if ((private && !ctx->rsa.prikey) || !ctx->rsa.pubkey) {
|
|
ret = -EINVAL;
|
|
goto free;
|
|
}
|
|
|
|
ctx->fallback = false;
|
|
return 0;
|
|
|
|
free:
|
|
hpre_rsa_clear_ctx(ctx, false);
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_rsa_setpubkey(struct crypto_akcipher *tfm, const void *key,
|
|
unsigned int keylen)
|
|
{
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
ret = crypto_akcipher_set_pub_key(ctx->rsa.soft_tfm, key, keylen);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!ctx->qp)
|
|
return 0;
|
|
|
|
return hpre_rsa_setkey(ctx, key, keylen, false);
|
|
}
|
|
|
|
static int hpre_rsa_setprivkey(struct crypto_akcipher *tfm, const void *key,
|
|
unsigned int keylen)
|
|
{
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
ret = crypto_akcipher_set_priv_key(ctx->rsa.soft_tfm, key, keylen);
|
|
if (ret)
|
|
return ret;
|
|
|
|
if (!ctx->qp)
|
|
return 0;
|
|
|
|
return hpre_rsa_setkey(ctx, key, keylen, true);
|
|
}
|
|
|
|
static unsigned int hpre_rsa_max_size(struct crypto_akcipher *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
|
|
/* For unsupported key size and unavailable devices, use soft tfm instead */
|
|
if (ctx->fallback)
|
|
return crypto_akcipher_maxsize(ctx->rsa.soft_tfm);
|
|
|
|
return ctx->key_sz;
|
|
}
|
|
|
|
static int hpre_rsa_init_tfm(struct crypto_akcipher *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
ctx->rsa.soft_tfm = crypto_alloc_akcipher("rsa-generic", 0, 0);
|
|
if (IS_ERR(ctx->rsa.soft_tfm)) {
|
|
pr_err("Can not alloc_akcipher!\n");
|
|
return PTR_ERR(ctx->rsa.soft_tfm);
|
|
}
|
|
|
|
akcipher_set_reqsize(tfm, sizeof(struct hpre_asym_request) +
|
|
hpre_align_pd());
|
|
|
|
ret = hpre_ctx_init(ctx, HPRE_V2_ALG_TYPE);
|
|
if (ret && ret != -ENODEV) {
|
|
crypto_free_akcipher(ctx->rsa.soft_tfm);
|
|
return ret;
|
|
} else if (ret == -ENODEV) {
|
|
ctx->fallback = true;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hpre_rsa_exit_tfm(struct crypto_akcipher *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = akcipher_tfm_ctx(tfm);
|
|
|
|
hpre_rsa_clear_ctx(ctx, true);
|
|
crypto_free_akcipher(ctx->rsa.soft_tfm);
|
|
}
|
|
|
|
static void hpre_key_to_big_end(u8 *data, int len)
|
|
{
|
|
int i, j;
|
|
|
|
for (i = 0; i < len / 2; i++) {
|
|
j = len - i - 1;
|
|
swap(data[j], data[i]);
|
|
}
|
|
}
|
|
|
|
static void hpre_ecc_clear_ctx(struct hpre_ctx *ctx, bool is_clear_all)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
unsigned int sz = ctx->key_sz;
|
|
unsigned int shift = sz << 1;
|
|
|
|
if (ctx->ecdh.p) {
|
|
/* ecdh: p->a->k->b */
|
|
memzero_explicit(ctx->ecdh.p + shift, sz);
|
|
dma_free_coherent(dev, sz << 3, ctx->ecdh.p, ctx->ecdh.dma_p);
|
|
ctx->ecdh.p = NULL;
|
|
}
|
|
|
|
hpre_ctx_clear(ctx, is_clear_all);
|
|
}
|
|
|
|
/*
|
|
* The bits of 192/224/256/384/521 are supported by HPRE,
|
|
* and convert the bits like:
|
|
* bits<=256, bits=256; 256<bits<=384, bits=384; 384<bits<=576, bits=576;
|
|
* If the parameter bit width is insufficient, then we fill in the
|
|
* high-order zeros by soft, so TASK_LENGTH1 is 0x3/0x5/0x8;
|
|
*/
|
|
static unsigned int hpre_ecdh_supported_curve(unsigned short id)
|
|
{
|
|
switch (id) {
|
|
case ECC_CURVE_NIST_P192:
|
|
case ECC_CURVE_NIST_P256:
|
|
return HPRE_ECC_HW256_KSZ_B;
|
|
case ECC_CURVE_NIST_P384:
|
|
return HPRE_ECC_HW384_KSZ_B;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void fill_curve_param(void *addr, u64 *param, unsigned int cur_sz, u8 ndigits)
|
|
{
|
|
unsigned int sz = cur_sz - (ndigits - 1) * sizeof(u64);
|
|
u8 i = 0;
|
|
|
|
while (i < ndigits - 1) {
|
|
memcpy(addr + sizeof(u64) * i, ¶m[i], sizeof(u64));
|
|
i++;
|
|
}
|
|
|
|
memcpy(addr + sizeof(u64) * i, ¶m[ndigits - 1], sz);
|
|
hpre_key_to_big_end((u8 *)addr, cur_sz);
|
|
}
|
|
|
|
static int hpre_ecdh_fill_curve(struct hpre_ctx *ctx, struct ecdh *params,
|
|
unsigned int cur_sz)
|
|
{
|
|
unsigned int shifta = ctx->key_sz << 1;
|
|
unsigned int shiftb = ctx->key_sz << 2;
|
|
void *p = ctx->ecdh.p + ctx->key_sz - cur_sz;
|
|
void *a = ctx->ecdh.p + shifta - cur_sz;
|
|
void *b = ctx->ecdh.p + shiftb - cur_sz;
|
|
void *x = ctx->ecdh.g + ctx->key_sz - cur_sz;
|
|
void *y = ctx->ecdh.g + shifta - cur_sz;
|
|
const struct ecc_curve *curve = ecc_get_curve(ctx->curve_id);
|
|
char *n;
|
|
|
|
if (unlikely(!curve))
|
|
return -EINVAL;
|
|
|
|
n = kzalloc(ctx->key_sz, GFP_KERNEL);
|
|
if (!n)
|
|
return -ENOMEM;
|
|
|
|
fill_curve_param(p, curve->p, cur_sz, curve->g.ndigits);
|
|
fill_curve_param(a, curve->a, cur_sz, curve->g.ndigits);
|
|
fill_curve_param(b, curve->b, cur_sz, curve->g.ndigits);
|
|
fill_curve_param(x, curve->g.x, cur_sz, curve->g.ndigits);
|
|
fill_curve_param(y, curve->g.y, cur_sz, curve->g.ndigits);
|
|
fill_curve_param(n, curve->n, cur_sz, curve->g.ndigits);
|
|
|
|
if (params->key_size == cur_sz && memcmp(params->key, n, cur_sz) >= 0) {
|
|
kfree(n);
|
|
return -EINVAL;
|
|
}
|
|
|
|
kfree(n);
|
|
return 0;
|
|
}
|
|
|
|
static unsigned int hpre_ecdh_get_curvesz(unsigned short id)
|
|
{
|
|
switch (id) {
|
|
case ECC_CURVE_NIST_P192:
|
|
return HPRE_ECC_NIST_P192_N_SIZE;
|
|
case ECC_CURVE_NIST_P256:
|
|
return HPRE_ECC_NIST_P256_N_SIZE;
|
|
case ECC_CURVE_NIST_P384:
|
|
return HPRE_ECC_NIST_P384_N_SIZE;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_ecdh_set_param(struct hpre_ctx *ctx, struct ecdh *params)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
unsigned int sz, shift, curve_sz;
|
|
int ret;
|
|
|
|
ctx->key_sz = hpre_ecdh_supported_curve(ctx->curve_id);
|
|
if (!ctx->key_sz)
|
|
return -EINVAL;
|
|
|
|
curve_sz = hpre_ecdh_get_curvesz(ctx->curve_id);
|
|
if (!curve_sz || params->key_size > curve_sz)
|
|
return -EINVAL;
|
|
|
|
sz = ctx->key_sz;
|
|
|
|
if (!ctx->ecdh.p) {
|
|
ctx->ecdh.p = dma_alloc_coherent(dev, sz << 3, &ctx->ecdh.dma_p,
|
|
GFP_KERNEL);
|
|
if (!ctx->ecdh.p)
|
|
return -ENOMEM;
|
|
}
|
|
|
|
shift = sz << 2;
|
|
ctx->ecdh.g = ctx->ecdh.p + shift;
|
|
ctx->ecdh.dma_g = ctx->ecdh.dma_p + shift;
|
|
|
|
ret = hpre_ecdh_fill_curve(ctx, params, curve_sz);
|
|
if (ret) {
|
|
dev_err(dev, "failed to fill curve_param, ret = %d!\n", ret);
|
|
dma_free_coherent(dev, sz << 3, ctx->ecdh.p, ctx->ecdh.dma_p);
|
|
ctx->ecdh.p = NULL;
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static bool hpre_key_is_zero(const char *key, unsigned short key_sz)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < key_sz; i++)
|
|
if (key[i])
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static int ecdh_gen_privkey(struct hpre_ctx *ctx, struct ecdh *params)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
int ret;
|
|
|
|
ret = crypto_get_default_rng();
|
|
if (ret) {
|
|
dev_err(dev, "failed to get default rng, ret = %d!\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = crypto_rng_get_bytes(crypto_default_rng, (u8 *)params->key,
|
|
params->key_size);
|
|
crypto_put_default_rng();
|
|
if (ret)
|
|
dev_err(dev, "failed to get rng, ret = %d!\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_ecdh_set_secret(struct crypto_kpp *tfm, const void *buf,
|
|
unsigned int len)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
unsigned int sz, sz_shift, curve_sz;
|
|
struct device *dev = ctx->dev;
|
|
char key[HPRE_ECC_MAX_KSZ];
|
|
struct ecdh params;
|
|
int ret;
|
|
|
|
if (ctx->fallback)
|
|
return crypto_kpp_set_secret(ctx->ecdh.soft_tfm, buf, len);
|
|
|
|
if (crypto_ecdh_decode_key(buf, len, ¶ms) < 0) {
|
|
dev_err(dev, "failed to decode ecdh key!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Use stdrng to generate private key */
|
|
if (!params.key || !params.key_size) {
|
|
params.key = key;
|
|
curve_sz = hpre_ecdh_get_curvesz(ctx->curve_id);
|
|
if (!curve_sz) {
|
|
dev_err(dev, "Invalid curve size!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
params.key_size = curve_sz - 1;
|
|
ret = ecdh_gen_privkey(ctx, ¶ms);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
if (hpre_key_is_zero(params.key, params.key_size)) {
|
|
dev_err(dev, "Invalid hpre key!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
hpre_ecc_clear_ctx(ctx, false);
|
|
|
|
ret = hpre_ecdh_set_param(ctx, ¶ms);
|
|
if (ret < 0) {
|
|
dev_err(dev, "failed to set hpre param, ret = %d!\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
sz = ctx->key_sz;
|
|
sz_shift = (sz << 1) + sz - params.key_size;
|
|
memcpy(ctx->ecdh.p + sz_shift, params.key, params.key_size);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static void hpre_ecdh_hw_data_clr_all(struct hpre_ctx *ctx,
|
|
struct hpre_asym_request *req,
|
|
struct scatterlist *dst,
|
|
struct scatterlist *src)
|
|
{
|
|
struct device *dev = ctx->dev;
|
|
struct hpre_sqe *sqe = &req->req;
|
|
dma_addr_t dma;
|
|
|
|
dma = le64_to_cpu(sqe->in);
|
|
if (unlikely(dma_mapping_error(dev, dma)))
|
|
return;
|
|
|
|
if (src && req->src)
|
|
dma_free_coherent(dev, ctx->key_sz << 2, req->src, dma);
|
|
|
|
dma = le64_to_cpu(sqe->out);
|
|
if (unlikely(dma_mapping_error(dev, dma)))
|
|
return;
|
|
|
|
if (req->dst)
|
|
dma_free_coherent(dev, ctx->key_sz << 1, req->dst, dma);
|
|
if (dst)
|
|
dma_unmap_single(dev, dma, ctx->key_sz << 1, DMA_FROM_DEVICE);
|
|
}
|
|
|
|
static void hpre_ecdh_cb(struct hpre_ctx *ctx, void *resp)
|
|
{
|
|
unsigned int curve_sz = hpre_ecdh_get_curvesz(ctx->curve_id);
|
|
struct hpre_dfx *dfx = ctx->hpre->debug.dfx;
|
|
struct hpre_asym_request *req = NULL;
|
|
struct kpp_request *areq;
|
|
u64 overtime_thrhld;
|
|
char *p;
|
|
int ret;
|
|
|
|
ret = hpre_alg_res_post_hf(ctx, resp, (void **)&req);
|
|
areq = req->areq.ecdh;
|
|
areq->dst_len = ctx->key_sz << 1;
|
|
|
|
overtime_thrhld = atomic64_read(&dfx[HPRE_OVERTIME_THRHLD].value);
|
|
if (overtime_thrhld && hpre_is_bd_timeout(req, overtime_thrhld))
|
|
atomic64_inc(&dfx[HPRE_OVER_THRHLD_CNT].value);
|
|
|
|
/* Do unmap before data processing */
|
|
hpre_ecdh_hw_data_clr_all(ctx, req, areq->dst, areq->src);
|
|
|
|
p = sg_virt(areq->dst);
|
|
memmove(p, p + ctx->key_sz - curve_sz, curve_sz);
|
|
memmove(p + curve_sz, p + areq->dst_len - curve_sz, curve_sz);
|
|
|
|
kpp_request_complete(areq, ret);
|
|
|
|
atomic64_inc(&dfx[HPRE_RECV_CNT].value);
|
|
}
|
|
|
|
static int hpre_ecdh_msg_request_set(struct hpre_ctx *ctx,
|
|
struct kpp_request *req)
|
|
{
|
|
struct hpre_asym_request *h_req;
|
|
struct hpre_sqe *msg;
|
|
void *tmp;
|
|
|
|
if (req->dst_len < ctx->key_sz << 1) {
|
|
req->dst_len = ctx->key_sz << 1;
|
|
return -EINVAL;
|
|
}
|
|
|
|
tmp = kpp_request_ctx(req);
|
|
h_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
h_req->cb = hpre_ecdh_cb;
|
|
h_req->areq.ecdh = req;
|
|
msg = &h_req->req;
|
|
memset(msg, 0, sizeof(*msg));
|
|
msg->in = cpu_to_le64(DMA_MAPPING_ERROR);
|
|
msg->out = cpu_to_le64(DMA_MAPPING_ERROR);
|
|
msg->key = cpu_to_le64(ctx->ecdh.dma_p);
|
|
|
|
msg->dw0 |= cpu_to_le32(0x1U << HPRE_SQE_DONE_SHIFT);
|
|
msg->task_len1 = (ctx->key_sz >> HPRE_BITS_2_BYTES_SHIFT) - 1;
|
|
h_req->ctx = ctx;
|
|
|
|
hpre_dfx_add_req_time(h_req);
|
|
msg->tag = cpu_to_le64((uintptr_t)h_req);
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_ecdh_src_data_init(struct hpre_asym_request *hpre_req,
|
|
struct scatterlist *data, unsigned int len)
|
|
{
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
struct hpre_ctx *ctx = hpre_req->ctx;
|
|
struct device *dev = ctx->dev;
|
|
unsigned int tmpshift;
|
|
dma_addr_t dma = 0;
|
|
void *ptr;
|
|
int shift;
|
|
|
|
/* Src_data include gx and gy. */
|
|
shift = ctx->key_sz - (len >> 1);
|
|
if (unlikely(shift < 0))
|
|
return -EINVAL;
|
|
|
|
ptr = dma_alloc_coherent(dev, ctx->key_sz << 2, &dma, GFP_KERNEL);
|
|
if (unlikely(!ptr))
|
|
return -ENOMEM;
|
|
|
|
tmpshift = ctx->key_sz << 1;
|
|
scatterwalk_map_and_copy(ptr + tmpshift, data, 0, len, 0);
|
|
memcpy(ptr + shift, ptr + tmpshift, len >> 1);
|
|
memcpy(ptr + ctx->key_sz + shift, ptr + tmpshift + (len >> 1), len >> 1);
|
|
|
|
hpre_req->src = ptr;
|
|
msg->in = cpu_to_le64(dma);
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_ecdh_dst_data_init(struct hpre_asym_request *hpre_req,
|
|
struct scatterlist *data, unsigned int len)
|
|
{
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
struct hpre_ctx *ctx = hpre_req->ctx;
|
|
struct device *dev = ctx->dev;
|
|
dma_addr_t dma;
|
|
|
|
if (unlikely(!data || !sg_is_last(data) || len != ctx->key_sz << 1)) {
|
|
dev_err(dev, "data or data length is illegal!\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
hpre_req->dst = NULL;
|
|
dma = dma_map_single(dev, sg_virt(data), len, DMA_FROM_DEVICE);
|
|
if (unlikely(dma_mapping_error(dev, dma))) {
|
|
dev_err(dev, "dma map data err!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
msg->out = cpu_to_le64(dma);
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_ecdh_compute_value(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
struct device *dev = ctx->dev;
|
|
void *tmp = kpp_request_ctx(req);
|
|
struct hpre_asym_request *hpre_req = PTR_ALIGN(tmp, hpre_align_sz());
|
|
struct hpre_sqe *msg = &hpre_req->req;
|
|
int ret;
|
|
|
|
ret = hpre_ecdh_msg_request_set(ctx, req);
|
|
if (unlikely(ret)) {
|
|
dev_err(dev, "failed to set ecdh request, ret = %d!\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
if (req->src) {
|
|
ret = hpre_ecdh_src_data_init(hpre_req, req->src, req->src_len);
|
|
if (unlikely(ret)) {
|
|
dev_err(dev, "failed to init src data, ret = %d!\n", ret);
|
|
goto clear_all;
|
|
}
|
|
} else {
|
|
msg->in = cpu_to_le64(ctx->ecdh.dma_g);
|
|
}
|
|
|
|
ret = hpre_ecdh_dst_data_init(hpre_req, req->dst, req->dst_len);
|
|
if (unlikely(ret)) {
|
|
dev_err(dev, "failed to init dst data, ret = %d!\n", ret);
|
|
goto clear_all;
|
|
}
|
|
|
|
msg->dw0 = cpu_to_le32(le32_to_cpu(msg->dw0) | HPRE_ALG_ECC_MUL);
|
|
msg->resv1 = ctx->enable_hpcore << HPRE_ENABLE_HPCORE_SHIFT;
|
|
|
|
ret = hpre_send(ctx, msg);
|
|
if (likely(!ret))
|
|
return -EINPROGRESS;
|
|
|
|
clear_all:
|
|
hpre_ecdh_hw_data_clr_all(ctx, hpre_req, req->dst, req->src);
|
|
return ret;
|
|
}
|
|
|
|
static int hpre_ecdh_generate_public_key(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
if (ctx->fallback) {
|
|
kpp_request_set_tfm(req, ctx->ecdh.soft_tfm);
|
|
ret = crypto_kpp_generate_public_key(req);
|
|
kpp_request_set_tfm(req, tfm);
|
|
return ret;
|
|
}
|
|
|
|
return hpre_ecdh_compute_value(req);
|
|
}
|
|
|
|
static int hpre_ecdh_compute_shared_secret(struct kpp_request *req)
|
|
{
|
|
struct crypto_kpp *tfm = crypto_kpp_reqtfm(req);
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
int ret;
|
|
|
|
if (ctx->fallback) {
|
|
kpp_request_set_tfm(req, ctx->ecdh.soft_tfm);
|
|
ret = crypto_kpp_compute_shared_secret(req);
|
|
kpp_request_set_tfm(req, tfm);
|
|
return ret;
|
|
}
|
|
|
|
return hpre_ecdh_compute_value(req);
|
|
}
|
|
|
|
static unsigned int hpre_ecdh_max_size(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
if (ctx->fallback)
|
|
return crypto_kpp_maxsize(ctx->ecdh.soft_tfm);
|
|
|
|
/* max size is the pub_key_size, include x and y */
|
|
return ctx->key_sz << 1;
|
|
}
|
|
|
|
static int hpre_ecdh_init_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
const char *alg = kpp_alg_name(tfm);
|
|
int ret;
|
|
|
|
ret = hpre_ctx_init(ctx, HPRE_V3_ECC_ALG_TYPE);
|
|
if (!ret) {
|
|
kpp_set_reqsize(tfm, sizeof(struct hpre_asym_request) + hpre_align_pd());
|
|
return 0;
|
|
} else if (ret && ret != -ENODEV) {
|
|
return ret;
|
|
}
|
|
|
|
ctx->ecdh.soft_tfm = crypto_alloc_kpp(alg, 0, CRYPTO_ALG_NEED_FALLBACK);
|
|
if (IS_ERR(ctx->ecdh.soft_tfm)) {
|
|
pr_err("Failed to alloc %s tfm!\n", alg);
|
|
return PTR_ERR(ctx->ecdh.soft_tfm);
|
|
}
|
|
|
|
crypto_kpp_set_flags(ctx->ecdh.soft_tfm, crypto_kpp_get_flags(tfm));
|
|
ctx->fallback = true;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int hpre_ecdh_nist_p192_init_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
ctx->curve_id = ECC_CURVE_NIST_P192;
|
|
|
|
return hpre_ecdh_init_tfm(tfm);
|
|
}
|
|
|
|
static int hpre_ecdh_nist_p256_init_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
ctx->curve_id = ECC_CURVE_NIST_P256;
|
|
ctx->enable_hpcore = 1;
|
|
|
|
return hpre_ecdh_init_tfm(tfm);
|
|
}
|
|
|
|
static int hpre_ecdh_nist_p384_init_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
ctx->curve_id = ECC_CURVE_NIST_P384;
|
|
|
|
return hpre_ecdh_init_tfm(tfm);
|
|
}
|
|
|
|
static void hpre_ecdh_exit_tfm(struct crypto_kpp *tfm)
|
|
{
|
|
struct hpre_ctx *ctx = kpp_tfm_ctx(tfm);
|
|
|
|
if (ctx->fallback) {
|
|
crypto_free_kpp(ctx->ecdh.soft_tfm);
|
|
return;
|
|
}
|
|
|
|
hpre_ecc_clear_ctx(ctx, true);
|
|
}
|
|
|
|
static struct akcipher_alg rsa = {
|
|
.encrypt = hpre_rsa_enc,
|
|
.decrypt = hpre_rsa_dec,
|
|
.set_pub_key = hpre_rsa_setpubkey,
|
|
.set_priv_key = hpre_rsa_setprivkey,
|
|
.max_size = hpre_rsa_max_size,
|
|
.init = hpre_rsa_init_tfm,
|
|
.exit = hpre_rsa_exit_tfm,
|
|
.base = {
|
|
.cra_ctxsize = sizeof(struct hpre_ctx),
|
|
.cra_priority = HPRE_CRYPTO_ALG_PRI,
|
|
.cra_name = "rsa",
|
|
.cra_driver_name = "hpre-rsa",
|
|
.cra_module = THIS_MODULE,
|
|
.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
|
|
},
|
|
};
|
|
|
|
static struct kpp_alg dh = {
|
|
.set_secret = hpre_dh_set_secret,
|
|
.generate_public_key = hpre_dh_generate_public_key,
|
|
.compute_shared_secret = hpre_dh_compute_shared_secret,
|
|
.max_size = hpre_dh_max_size,
|
|
.init = hpre_dh_init_tfm,
|
|
.exit = hpre_dh_exit_tfm,
|
|
.base = {
|
|
.cra_ctxsize = sizeof(struct hpre_ctx),
|
|
.cra_priority = HPRE_CRYPTO_ALG_PRI,
|
|
.cra_name = "dh",
|
|
.cra_driver_name = "hpre-dh",
|
|
.cra_module = THIS_MODULE,
|
|
.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
|
|
},
|
|
};
|
|
|
|
static struct kpp_alg ecdh_curves[] = {
|
|
{
|
|
.set_secret = hpre_ecdh_set_secret,
|
|
.generate_public_key = hpre_ecdh_generate_public_key,
|
|
.compute_shared_secret = hpre_ecdh_compute_shared_secret,
|
|
.max_size = hpre_ecdh_max_size,
|
|
.init = hpre_ecdh_nist_p192_init_tfm,
|
|
.exit = hpre_ecdh_exit_tfm,
|
|
.base = {
|
|
.cra_ctxsize = sizeof(struct hpre_ctx),
|
|
.cra_priority = HPRE_CRYPTO_ALG_PRI,
|
|
.cra_name = "ecdh-nist-p192",
|
|
.cra_driver_name = "hpre-ecdh-nist-p192",
|
|
.cra_module = THIS_MODULE,
|
|
.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
|
|
},
|
|
}, {
|
|
.set_secret = hpre_ecdh_set_secret,
|
|
.generate_public_key = hpre_ecdh_generate_public_key,
|
|
.compute_shared_secret = hpre_ecdh_compute_shared_secret,
|
|
.max_size = hpre_ecdh_max_size,
|
|
.init = hpre_ecdh_nist_p256_init_tfm,
|
|
.exit = hpre_ecdh_exit_tfm,
|
|
.base = {
|
|
.cra_ctxsize = sizeof(struct hpre_ctx),
|
|
.cra_priority = HPRE_CRYPTO_ALG_PRI,
|
|
.cra_name = "ecdh-nist-p256",
|
|
.cra_driver_name = "hpre-ecdh-nist-p256",
|
|
.cra_module = THIS_MODULE,
|
|
.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
|
|
},
|
|
}, {
|
|
.set_secret = hpre_ecdh_set_secret,
|
|
.generate_public_key = hpre_ecdh_generate_public_key,
|
|
.compute_shared_secret = hpre_ecdh_compute_shared_secret,
|
|
.max_size = hpre_ecdh_max_size,
|
|
.init = hpre_ecdh_nist_p384_init_tfm,
|
|
.exit = hpre_ecdh_exit_tfm,
|
|
.base = {
|
|
.cra_ctxsize = sizeof(struct hpre_ctx),
|
|
.cra_priority = HPRE_CRYPTO_ALG_PRI,
|
|
.cra_name = "ecdh-nist-p384",
|
|
.cra_driver_name = "hpre-ecdh-nist-p384",
|
|
.cra_module = THIS_MODULE,
|
|
.cra_flags = CRYPTO_ALG_NEED_FALLBACK,
|
|
},
|
|
}
|
|
};
|
|
|
|
static int hpre_register_rsa(struct hisi_qm *qm)
|
|
{
|
|
int ret;
|
|
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_RSA_MASK_CAP))
|
|
return 0;
|
|
|
|
rsa.base.cra_flags = 0;
|
|
ret = crypto_register_akcipher(&rsa);
|
|
if (ret)
|
|
dev_err(&qm->pdev->dev, "failed to register rsa (%d)!\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void hpre_unregister_rsa(struct hisi_qm *qm)
|
|
{
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_RSA_MASK_CAP))
|
|
return;
|
|
|
|
crypto_unregister_akcipher(&rsa);
|
|
}
|
|
|
|
static int hpre_register_dh(struct hisi_qm *qm)
|
|
{
|
|
int ret;
|
|
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_DH_MASK_CAP))
|
|
return 0;
|
|
|
|
ret = crypto_register_kpp(&dh);
|
|
if (ret)
|
|
dev_err(&qm->pdev->dev, "failed to register dh (%d)!\n", ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void hpre_unregister_dh(struct hisi_qm *qm)
|
|
{
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_DH_MASK_CAP))
|
|
return;
|
|
|
|
crypto_unregister_kpp(&dh);
|
|
}
|
|
|
|
static int hpre_register_ecdh(struct hisi_qm *qm)
|
|
{
|
|
int ret, i;
|
|
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_ECDH_MASK_CAP))
|
|
return 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(ecdh_curves); i++) {
|
|
ret = crypto_register_kpp(&ecdh_curves[i]);
|
|
if (ret) {
|
|
dev_err(&qm->pdev->dev, "failed to register %s (%d)!\n",
|
|
ecdh_curves[i].base.cra_name, ret);
|
|
goto unreg_kpp;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
|
|
unreg_kpp:
|
|
for (--i; i >= 0; --i)
|
|
crypto_unregister_kpp(&ecdh_curves[i]);
|
|
|
|
return ret;
|
|
}
|
|
|
|
static void hpre_unregister_ecdh(struct hisi_qm *qm)
|
|
{
|
|
int i;
|
|
|
|
if (!hpre_check_alg_support(qm, HPRE_DRV_ECDH_MASK_CAP))
|
|
return;
|
|
|
|
for (i = ARRAY_SIZE(ecdh_curves) - 1; i >= 0; --i)
|
|
crypto_unregister_kpp(&ecdh_curves[i]);
|
|
}
|
|
|
|
int hpre_algs_register(struct hisi_qm *qm)
|
|
{
|
|
int ret = 0;
|
|
|
|
mutex_lock(&hpre_algs_lock);
|
|
if (hpre_available_devs) {
|
|
hpre_available_devs++;
|
|
goto unlock;
|
|
}
|
|
|
|
ret = hpre_register_rsa(qm);
|
|
if (ret)
|
|
goto unlock;
|
|
|
|
ret = hpre_register_dh(qm);
|
|
if (ret)
|
|
goto unreg_rsa;
|
|
|
|
ret = hpre_register_ecdh(qm);
|
|
if (ret)
|
|
goto unreg_dh;
|
|
|
|
hpre_available_devs++;
|
|
mutex_unlock(&hpre_algs_lock);
|
|
|
|
return ret;
|
|
|
|
unreg_dh:
|
|
hpre_unregister_dh(qm);
|
|
unreg_rsa:
|
|
hpre_unregister_rsa(qm);
|
|
unlock:
|
|
mutex_unlock(&hpre_algs_lock);
|
|
return ret;
|
|
}
|
|
|
|
void hpre_algs_unregister(struct hisi_qm *qm)
|
|
{
|
|
mutex_lock(&hpre_algs_lock);
|
|
if (--hpre_available_devs)
|
|
goto unlock;
|
|
|
|
hpre_unregister_ecdh(qm);
|
|
hpre_unregister_dh(qm);
|
|
hpre_unregister_rsa(qm);
|
|
|
|
unlock:
|
|
mutex_unlock(&hpre_algs_lock);
|
|
}
|