[PATCH RESEND v1 8/9] crypto/ycc: Add sm2 algorithm support

'Guanjun'
2022-09-06T03:36:58Z

From: Xuchun Shang <xuchun.shang@linux.alibaba.com>

Only support verification through sm2 at present.

Signed-off-by: Xuchun Shang <xuchun.shang@linux.alibaba.com>
---
drivers/crypto/ycc/Makefile | 3 +-
drivers/crypto/ycc/sm2signature_asn1.c | 38 +++++
drivers/crypto/ycc/sm2signature_asn1.h | 13 ++
drivers/crypto/ycc/ycc_algs.h | 2 +
drivers/crypto/ycc/ycc_pke.c | 250 ++++++++++++++++++++++++++++++++-
drivers/crypto/ycc/ycc_ring.h | 8 ++
6 files changed, 312 insertions(+), 2 deletions(-)
create mode 100644 drivers/crypto/ycc/sm2signature_asn1.c
create mode 100644 drivers/crypto/ycc/sm2signature_asn1.h

diff --git a/drivers/crypto/ycc/Makefile b/drivers/crypto/ycc/Makefile
index 89d6d6f..4f95c7f 100644
--- a/drivers/crypto/ycc/Makefile
+++ b/drivers/crypto/ycc/Makefile
@@ -1,3 +1,4 @@
# SPDX-License-Identifier: GPL-2.0
obj-(CONFIG_CRYPTO_DEV_YCC) += ycc.o
-ycc-objs := ycc_drv.o ycc_isr.o ycc_cdev.o ycc_ring.o ycc_ske.o ycc_aead.o ycc_pke.o
+ycc-objs := ycc_drv.o ycc_isr.o ycc_cdev.o ycc_ring.o ycc_ske.o ycc_aead.o \
+ ycc_pke.o sm2signature_asn1.o
diff --git a/drivers/crypto/ycc/sm2signature_asn1.c b/drivers/crypto/ycc/sm2signature_asn1.c
new file mode 100644
index 00000000..1fd15c1
--- /dev/null
+++ b/drivers/crypto/ycc/sm2signature_asn1.c
@@ -0,0 +1,38 @@
+/*
+ * Automatically generated by asn1_compiler. Do not edit
+ *
+ * ASN.1 parser for sm2signature
+ */
+#include <linux/asn1_ber_bytecode.h>
+#include "sm2signature_asn1.h"
+
+enum sm2signature_actions {
+ ACT_sm2_get_signature_r = 0,
+ ACT_sm2_get_signature_s = 1,
+ NR__sm2signature_actions = 2
+};
+
+static const asn1_action_t sm2signature_action_table[NR__sm2signature_actions] = {
+ [0] = sm2_get_signature_r,
+ [1] = sm2_get_signature_s,
+};
+
+static const unsigned char sm2signature_machine[] = {
+ // Sm2Signature
+ [0] = ASN1_OP_MATCH,
+ [1] = _tag(UNIV, CONS, SEQ),
+ [2] = ASN1_OP_MATCH_ACT, // sig_r
+ [3] = _tag(UNIV, PRIM, INT),
+ [4] = _action(ACT_sm2_get_signature_r),
+ [5] = ASN1_OP_MATCH_ACT, // sig_s
+ [6] = _tag(UNIV, PRIM, INT),
+ [7] = _action(ACT_sm2_get_signature_s),
+ [8] = ASN1_OP_END_SEQ,
+ [9] = ASN1_OP_COMPLETE,
+};
+
+const struct asn1_decoder sm2signature_decoder = {
+ .machine = sm2signature_machine,
+ .machlen = sizeof(sm2signature_machine),
+ .actions = sm2signature_action_table,
+};
diff --git a/drivers/crypto/ycc/sm2signature_asn1.h b/drivers/crypto/ycc/sm2signature_asn1.h
new file mode 100644
index 00000000..192c9e1
--- /dev/null
+++ b/drivers/crypto/ycc/sm2signature_asn1.h
@@ -0,0 +1,13 @@
+/*
+ * Automatically generated by asn1_compiler. Do not edit
+ *
+ * ASN.1 parser for sm2signature
+ */
+#include <linux/asn1_decoder.h>
+
+extern const struct asn1_decoder sm2signature_decoder;
+
+extern int sm2_get_signature_r(void *context, size_t hdrlen,
+ unsigned char tag, const void *value, size_t vlen);
+extern int sm2_get_signature_s(void *context, size_t hdrlen,
+ unsigned char tag, const void *value, size_t vlen);
diff --git a/drivers/crypto/ycc/ycc_algs.h b/drivers/crypto/ycc/ycc_algs.h
index 6a13230a..26323a8 100644
--- a/drivers/crypto/ycc/ycc_algs.h
+++ b/drivers/crypto/ycc/ycc_algs.h
@@ -77,6 +77,8 @@ enum ycc_cmd_id {
YCC_CMD_CCM_ENC,
YCC_CMD_CCM_DEC, /* 0x28 */

+ YCC_CMD_SM2_VERIFY = 0x47,
+
YCC_CMD_RSA_ENC = 0x83,
YCC_CMD_RSA_DEC,
YCC_CMD_RSA_CRT_DEC,
diff --git a/drivers/crypto/ycc/ycc_pke.c b/drivers/crypto/ycc/ycc_pke.c
index 3debd80..41d27d6 100644
--- a/drivers/crypto/ycc/ycc_pke.c
+++ b/drivers/crypto/ycc/ycc_pke.c
@@ -8,6 +8,8 @@
#include <linux/dma-mapping.h>
#include <linux/crypto.h>
#include <linux/mpi.h>
+
+#include "sm2signature_asn1.h"
#include "ycc_algs.h"

static int ycc_rsa_done_callback(void *ptr, u16 state)
@@ -666,6 +668,222 @@ static void ycc_rsa_exit(struct crypto_akcipher *tfm)
crypto_free_akcipher(ctx->soft_tfm);
}

+#define MPI_NBYTES(m) ((mpi_get_nbits(m) + 7) / 8)
+
+static int ycc_sm2_done_callback(void *ptr, u16 state)
+{
+ struct ycc_pke_req *sm2_req = (struct ycc_pke_req *)ptr;
+ struct ycc_pke_ctx *ctx = sm2_req->ctx;
+ struct akcipher_request *req = sm2_req->req;
+ struct device *dev = YCC_DEV(ctx);
+
+ dma_free_coherent(dev, 128, sm2_req->src_vaddr, sm2_req->src_paddr);
+
+ if (req->base.complete)
+ req->base.complete(&req->base, state == CMD_SUCCESS ? 0 : -EBADMSG);
+ return 0;
+}
+
+struct sm2_signature_ctx {
+ MPI sig_r;
+ MPI sig_s;
+};
+
+int sm2_get_signature_r(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct sm2_signature_ctx *sig = context;
+
+ if (!value || !vlen)
+ return -EINVAL;
+
+ sig->sig_r = mpi_read_raw_data(value, vlen);
+ if (!sig->sig_r)
+ return -ENOMEM;
+
+ return 0;
+}
+
+int sm2_get_signature_s(void *context, size_t hdrlen, unsigned char tag,
+ const void *value, size_t vlen)
+{
+ struct sm2_signature_ctx *sig = context;
+
+ if (!value || !vlen)
+ return -EINVAL;
+
+ sig->sig_s = mpi_read_raw_data(value, vlen);
+ if (!sig->sig_s)
+ return -ENOMEM;
+
+ return 0;
+}
+
+static int ycc_sm2_verify(struct akcipher_request *req)
+{
+ struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
+ struct ycc_pke_req *sm2_req = akcipher_request_ctx(req);
+ struct ycc_pke_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct ycc_sm2_verify_cmd *sm2_verify_cmd;
+ struct ycc_dev *ydev = ctx->ring->ydev;
+ struct ycc_ring *ring = ctx->ring;
+ struct device *dev = YCC_DEV(ctx);
+ struct sm2_signature_ctx sig;
+ struct ycc_flags *aflags;
+ u8 buffer[80] = {0};
+ int ret;
+
+ /* Do software fallback */
+ if (!test_bit(YDEV_STATUS_READY, &ydev->status) || ctx->key_len) {
+ akcipher_request_set_tfm(req, ctx->soft_tfm);
+ ret = crypto_akcipher_verify(req);
+ akcipher_request_set_tfm(req, tfm);
+ return ret;
+ }
+
+ if (req->src_len > 72 || req->src_len < 70 || req->dst_len != 32)
+ return -EINVAL;
+
+ sm2_req->ctx = ctx;
+ sm2_req->req = req;
+
+ sg_copy_buffer(req->src, sg_nents(req->src), buffer, req->src_len, 0, 1);
+ sig.sig_r = NULL;
+ sig.sig_s = NULL;
+ ret = asn1_ber_decoder(&sm2signature_decoder, &sig, buffer, req->src_len);
+ if (ret)
+ return -EINVAL;
+
+ ret = mpi_print(GCRYMPI_FMT_USG, buffer, MPI_NBYTES(sig.sig_r),
+ (size_t *)NULL, sig.sig_r);
+ if (ret)
+ return -EINVAL;
+
+ ret = mpi_print(GCRYMPI_FMT_USG, buffer + MPI_NBYTES(sig.sig_r),
+ MPI_NBYTES(sig.sig_s), (size_t *)NULL, sig.sig_s);
+ if (ret)
+ return -EINVAL;
+
+ ret = -ENOMEM;
+ /* Alloc dma for src, as verify has no output */
+ sm2_req->src_vaddr = dma_alloc_coherent(dev, 128, &sm2_req->src_paddr,
+ GFP_ATOMIC);
+ if (!sm2_req->src_vaddr)
+ goto out;
+
+ sg_copy_buffer(req->src, sg_nents(req->src), sm2_req->src_vaddr,
+ req->dst_len, req->src_len, 1);
+ memcpy(sm2_req->src_vaddr + 32, buffer, 64);
+
+ sm2_req->dst_vaddr = NULL;
+
+ aflags = kzalloc(sizeof(struct ycc_flags), GFP_ATOMIC);
+ if (!aflags)
+ goto free_src;
+
+ aflags->ptr = (void *)sm2_req;
+ aflags->ycc_done_callback = ycc_sm2_done_callback;
+
+ memset(&sm2_req->desc, 0, sizeof(sm2_req->desc));
+ sm2_req->desc.private_ptr = (u64)(void *)aflags;
+
+ sm2_verify_cmd = &sm2_req->desc.cmd.sm2_verify_cmd;
+ sm2_verify_cmd->cmd_id = YCC_CMD_SM2_VERIFY;
+ sm2_verify_cmd->sptr = sm2_req->src_paddr;
+ sm2_verify_cmd->keyptr = ctx->pub_key_paddr;
+
+ ret = ycc_enqueue(ring, (u8 *)&sm2_req->desc);
+ if (!ret)
+ return -EINPROGRESS;
+
+ kfree(aflags);
+free_src:
+ dma_free_coherent(dev, 128, sm2_req->src_vaddr, sm2_req->src_paddr);
+out:
+ return ret;
+}
+
+static unsigned int ycc_sm2_max_size(struct crypto_akcipher *tfm)
+{
+ return PAGE_SIZE;
+}
+
+static int ycc_sm2_setpubkey(struct crypto_akcipher *tfm, const void *key,
+ unsigned int keylen)
+{
+ struct ycc_pke_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct device *dev = YCC_DEV(ctx);
+ int ret;
+
+ ret = crypto_akcipher_set_pub_key(ctx->soft_tfm, key, keylen);
+ if (ret)
+ return ret;
+
+ /* Always alloc 64 bytes for pub key */
+ ctx->pub_key_vaddr = dma_alloc_coherent(dev, 64, &ctx->pub_key_paddr,
+ GFP_KERNEL);
+ if (!ctx->pub_key_vaddr)
+ return -ENOMEM;
+
+ /*
+ * Uncompressed key 65 bytes with 0x04 flag
+ * Compressed key 33 bytes with 0x02 or 0x03 flag
+ */
+ switch (keylen) {
+ case 65:
+ if (*(u8 *)key != 0x04)
+ return -EINVAL;
+ memcpy(ctx->pub_key_vaddr, key + 1, 64);
+ break;
+ case 64:
+ memcpy(ctx->pub_key_vaddr, key, 64);
+ break;
+ case 33:
+ return 0; /* TODO: use sw temporary */
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int ycc_sm2_init(struct crypto_akcipher *tfm)
+{
+ struct ycc_pke_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct ycc_ring *ring;
+
+ ctx->soft_tfm = crypto_alloc_akcipher("sm2-generic", 0, 0);
+ if (IS_ERR(ctx->soft_tfm))
+ return PTR_ERR(ctx->soft_tfm);
+
+ /* Reserve enough space if soft request reqires additional space */
+ akcipher_set_reqsize(tfm, sizeof(struct ycc_pke_req) +
+ crypto_akcipher_alg(ctx->soft_tfm)->reqsize);
+
+ ring = ycc_crypto_get_ring();
+ if (!ring) {
+ crypto_free_akcipher(ctx->soft_tfm);
+ return -ENODEV;
+ }
+
+ ctx->ring = ring;
+ return 0;
+}
+
+static void ycc_sm2_exit(struct crypto_akcipher *tfm)
+{
+ struct ycc_pke_ctx *ctx = akcipher_tfm_ctx(tfm);
+ struct device *dev = YCC_DEV(ctx);
+
+ if (ctx->ring)
+ ycc_crypto_free_ring(ctx->ring);
+
+ if (ctx->pub_key_vaddr)
+ dma_free_coherent(dev, 64, ctx->pub_key_vaddr, ctx->pub_key_paddr);
+
+ crypto_free_akcipher(ctx->soft_tfm);
+}
+
static struct akcipher_alg ycc_rsa = {
.base = {
.cra_name = "rsa",
@@ -685,12 +903,42 @@ static void ycc_rsa_exit(struct crypto_akcipher *tfm)
.exit = ycc_rsa_exit,
};

+static struct akcipher_alg ycc_sm2 = {
+ .base = {
+ .cra_name = "sm2",
+ .cra_driver_name = "sm2-ycc",
+ .cra_priority = 1000,
+ .cra_module = THIS_MODULE,
+ .cra_ctxsize = sizeof(struct ycc_pke_ctx),
+ },
+ .verify = ycc_sm2_verify,
+ .set_pub_key = ycc_sm2_setpubkey,
+ .max_size = ycc_sm2_max_size,
+ .init = ycc_sm2_init,
+ .exit = ycc_sm2_exit,
+};
+
int ycc_pke_register(void)
{
- return crypto_register_akcipher(&ycc_rsa);
+ int ret;
+
+ ret = crypto_register_akcipher(&ycc_rsa);
+ if (ret) {
+ pr_err("Failed to register rsa\n");
+ return ret;
+ }
+
+ ret = crypto_register_akcipher(&ycc_sm2);
+ if (ret) {
+ crypto_unregister_akcipher(&ycc_rsa);
+ pr_err("Failed to register sm2\n");
+ }
+
+ return ret;
}

void ycc_pke_unregister(void)
{
crypto_unregister_akcipher(&ycc_rsa);
+ crypto_unregister_akcipher(&ycc_sm2);
}
diff --git a/drivers/crypto/ycc/ycc_ring.h b/drivers/crypto/ycc/ycc_ring.h
index c47fc18..6dafdc7 100644
--- a/drivers/crypto/ycc/ycc_ring.h
+++ b/drivers/crypto/ycc/ycc_ring.h
@@ -121,11 +121,19 @@ struct ycc_rsa_dec_cmd {
u64 dptr:48;
} __packed;

+struct ycc_sm2_verify_cmd {
+ u8 cmd_id;
+ u64 sptr:48;
+ u16 key_id;
+ u64 keyptr:48;
+} __packed;
+
union ycc_real_cmd {
struct ycc_skcipher_cmd ske_cmd;
struct ycc_aead_cmd aead_cmd;
struct ycc_rsa_enc_cmd rsa_enc_cmd;
struct ycc_rsa_dec_cmd rsa_dec_cmd;
+ struct ycc_sm2_verify_cmd sm2_verify_cmd;
u8 padding[32];
};

--
1.8.3.1