162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * SM2 asymmetric public-key algorithm
462306a36Sopenharmony_ci * as specified by OSCCA GM/T 0003.1-2012 -- 0003.5-2012 SM2 and
562306a36Sopenharmony_ci * described at https://tools.ietf.org/html/draft-shen-sm2-ecdsa-02
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * Copyright (c) 2020, Alibaba Group.
862306a36Sopenharmony_ci * Authors: Tianjia Zhang <tianjia.zhang@linux.alibaba.com>
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/module.h>
1262306a36Sopenharmony_ci#include <linux/mpi.h>
1362306a36Sopenharmony_ci#include <crypto/internal/akcipher.h>
1462306a36Sopenharmony_ci#include <crypto/akcipher.h>
1562306a36Sopenharmony_ci#include <crypto/hash.h>
1662306a36Sopenharmony_ci#include <crypto/rng.h>
1762306a36Sopenharmony_ci#include <crypto/sm2.h>
1862306a36Sopenharmony_ci#include "sm2signature.asn1.h"
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* The default user id as specified in GM/T 0009-2012 */
2162306a36Sopenharmony_ci#define SM2_DEFAULT_USERID "1234567812345678"
2262306a36Sopenharmony_ci#define SM2_DEFAULT_USERID_LEN 16
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci#define MPI_NBYTES(m)   ((mpi_get_nbits(m) + 7) / 8)
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_cistruct ecc_domain_parms {
2762306a36Sopenharmony_ci	const char *desc;           /* Description of the curve.  */
2862306a36Sopenharmony_ci	unsigned int nbits;         /* Number of bits.  */
2962306a36Sopenharmony_ci	unsigned int fips:1; /* True if this is a FIPS140-2 approved curve */
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci	/* The model describing this curve.  This is mainly used to select
3262306a36Sopenharmony_ci	 * the group equation.
3362306a36Sopenharmony_ci	 */
3462306a36Sopenharmony_ci	enum gcry_mpi_ec_models model;
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci	/* The actual ECC dialect used.  This is used for curve specific
3762306a36Sopenharmony_ci	 * optimizations and to select encodings etc.
3862306a36Sopenharmony_ci	 */
3962306a36Sopenharmony_ci	enum ecc_dialects dialect;
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_ci	const char *p;              /* The prime defining the field.  */
4262306a36Sopenharmony_ci	const char *a, *b;          /* The coefficients.  For Twisted Edwards
4362306a36Sopenharmony_ci				     * Curves b is used for d.  For Montgomery
4462306a36Sopenharmony_ci				     * Curves (a,b) has ((A-2)/4,B^-1).
4562306a36Sopenharmony_ci				     */
4662306a36Sopenharmony_ci	const char *n;              /* The order of the base point.  */
4762306a36Sopenharmony_ci	const char *g_x, *g_y;      /* Base point.  */
4862306a36Sopenharmony_ci	unsigned int h;             /* Cofactor.  */
4962306a36Sopenharmony_ci};
5062306a36Sopenharmony_ci
5162306a36Sopenharmony_cistatic const struct ecc_domain_parms sm2_ecp = {
5262306a36Sopenharmony_ci	.desc = "sm2p256v1",
5362306a36Sopenharmony_ci	.nbits = 256,
5462306a36Sopenharmony_ci	.fips = 0,
5562306a36Sopenharmony_ci	.model = MPI_EC_WEIERSTRASS,
5662306a36Sopenharmony_ci	.dialect = ECC_DIALECT_STANDARD,
5762306a36Sopenharmony_ci	.p   = "0xfffffffeffffffffffffffffffffffffffffffff00000000ffffffffffffffff",
5862306a36Sopenharmony_ci	.a   = "0xfffffffeffffffffffffffffffffffffffffffff00000000fffffffffffffffc",
5962306a36Sopenharmony_ci	.b   = "0x28e9fa9e9d9f5e344d5a9e4bcf6509a7f39789f515ab8f92ddbcbd414d940e93",
6062306a36Sopenharmony_ci	.n   = "0xfffffffeffffffffffffffffffffffff7203df6b21c6052b53bbf40939d54123",
6162306a36Sopenharmony_ci	.g_x = "0x32c4ae2c1f1981195f9904466a39c9948fe30bbff2660be1715a4589334c74c7",
6262306a36Sopenharmony_ci	.g_y = "0xbc3736a2f4f6779c59bdcee36b692153d0a9877cc62a474002df32e52139f0a0",
6362306a36Sopenharmony_ci	.h = 1
6462306a36Sopenharmony_ci};
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_cistatic int __sm2_set_pub_key(struct mpi_ec_ctx *ec,
6762306a36Sopenharmony_ci			     const void *key, unsigned int keylen);
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_cistatic int sm2_ec_ctx_init(struct mpi_ec_ctx *ec)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	const struct ecc_domain_parms *ecp = &sm2_ecp;
7262306a36Sopenharmony_ci	MPI p, a, b;
7362306a36Sopenharmony_ci	MPI x, y;
7462306a36Sopenharmony_ci	int rc = -EINVAL;
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	p = mpi_scanval(ecp->p);
7762306a36Sopenharmony_ci	a = mpi_scanval(ecp->a);
7862306a36Sopenharmony_ci	b = mpi_scanval(ecp->b);
7962306a36Sopenharmony_ci	if (!p || !a || !b)
8062306a36Sopenharmony_ci		goto free_p;
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	x = mpi_scanval(ecp->g_x);
8362306a36Sopenharmony_ci	y = mpi_scanval(ecp->g_y);
8462306a36Sopenharmony_ci	if (!x || !y)
8562306a36Sopenharmony_ci		goto free;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	rc = -ENOMEM;
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ci	ec->Q = mpi_point_new(0);
9062306a36Sopenharmony_ci	if (!ec->Q)
9162306a36Sopenharmony_ci		goto free;
9262306a36Sopenharmony_ci
9362306a36Sopenharmony_ci	/* mpi_ec_setup_elliptic_curve */
9462306a36Sopenharmony_ci	ec->G = mpi_point_new(0);
9562306a36Sopenharmony_ci	if (!ec->G) {
9662306a36Sopenharmony_ci		mpi_point_release(ec->Q);
9762306a36Sopenharmony_ci		goto free;
9862306a36Sopenharmony_ci	}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_ci	mpi_set(ec->G->x, x);
10162306a36Sopenharmony_ci	mpi_set(ec->G->y, y);
10262306a36Sopenharmony_ci	mpi_set_ui(ec->G->z, 1);
10362306a36Sopenharmony_ci
10462306a36Sopenharmony_ci	rc = -EINVAL;
10562306a36Sopenharmony_ci	ec->n = mpi_scanval(ecp->n);
10662306a36Sopenharmony_ci	if (!ec->n) {
10762306a36Sopenharmony_ci		mpi_point_release(ec->Q);
10862306a36Sopenharmony_ci		mpi_point_release(ec->G);
10962306a36Sopenharmony_ci		goto free;
11062306a36Sopenharmony_ci	}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci	ec->h = ecp->h;
11362306a36Sopenharmony_ci	ec->name = ecp->desc;
11462306a36Sopenharmony_ci	mpi_ec_init(ec, ecp->model, ecp->dialect, 0, p, a, b);
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_ci	rc = 0;
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_cifree:
11962306a36Sopenharmony_ci	mpi_free(x);
12062306a36Sopenharmony_ci	mpi_free(y);
12162306a36Sopenharmony_cifree_p:
12262306a36Sopenharmony_ci	mpi_free(p);
12362306a36Sopenharmony_ci	mpi_free(a);
12462306a36Sopenharmony_ci	mpi_free(b);
12562306a36Sopenharmony_ci
12662306a36Sopenharmony_ci	return rc;
12762306a36Sopenharmony_ci}
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_cistatic void sm2_ec_ctx_deinit(struct mpi_ec_ctx *ec)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	mpi_ec_deinit(ec);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci	memset(ec, 0, sizeof(*ec));
13462306a36Sopenharmony_ci}
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/* RESULT must have been initialized and is set on success to the
13762306a36Sopenharmony_ci * point given by VALUE.
13862306a36Sopenharmony_ci */
13962306a36Sopenharmony_cistatic int sm2_ecc_os2ec(MPI_POINT result, MPI value)
14062306a36Sopenharmony_ci{
14162306a36Sopenharmony_ci	int rc;
14262306a36Sopenharmony_ci	size_t n;
14362306a36Sopenharmony_ci	unsigned char *buf;
14462306a36Sopenharmony_ci	MPI x, y;
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	n = MPI_NBYTES(value);
14762306a36Sopenharmony_ci	buf = kmalloc(n, GFP_KERNEL);
14862306a36Sopenharmony_ci	if (!buf)
14962306a36Sopenharmony_ci		return -ENOMEM;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	rc = mpi_print(GCRYMPI_FMT_USG, buf, n, &n, value);
15262306a36Sopenharmony_ci	if (rc)
15362306a36Sopenharmony_ci		goto err_freebuf;
15462306a36Sopenharmony_ci
15562306a36Sopenharmony_ci	rc = -EINVAL;
15662306a36Sopenharmony_ci	if (n < 1 || ((n - 1) % 2))
15762306a36Sopenharmony_ci		goto err_freebuf;
15862306a36Sopenharmony_ci	/* No support for point compression */
15962306a36Sopenharmony_ci	if (*buf != 0x4)
16062306a36Sopenharmony_ci		goto err_freebuf;
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ci	rc = -ENOMEM;
16362306a36Sopenharmony_ci	n = (n - 1) / 2;
16462306a36Sopenharmony_ci	x = mpi_read_raw_data(buf + 1, n);
16562306a36Sopenharmony_ci	if (!x)
16662306a36Sopenharmony_ci		goto err_freebuf;
16762306a36Sopenharmony_ci	y = mpi_read_raw_data(buf + 1 + n, n);
16862306a36Sopenharmony_ci	if (!y)
16962306a36Sopenharmony_ci		goto err_freex;
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	mpi_normalize(x);
17262306a36Sopenharmony_ci	mpi_normalize(y);
17362306a36Sopenharmony_ci	mpi_set(result->x, x);
17462306a36Sopenharmony_ci	mpi_set(result->y, y);
17562306a36Sopenharmony_ci	mpi_set_ui(result->z, 1);
17662306a36Sopenharmony_ci
17762306a36Sopenharmony_ci	rc = 0;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	mpi_free(y);
18062306a36Sopenharmony_cierr_freex:
18162306a36Sopenharmony_ci	mpi_free(x);
18262306a36Sopenharmony_cierr_freebuf:
18362306a36Sopenharmony_ci	kfree(buf);
18462306a36Sopenharmony_ci	return rc;
18562306a36Sopenharmony_ci}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_cistruct sm2_signature_ctx {
18862306a36Sopenharmony_ci	MPI sig_r;
18962306a36Sopenharmony_ci	MPI sig_s;
19062306a36Sopenharmony_ci};
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ciint sm2_get_signature_r(void *context, size_t hdrlen, unsigned char tag,
19362306a36Sopenharmony_ci				const void *value, size_t vlen)
19462306a36Sopenharmony_ci{
19562306a36Sopenharmony_ci	struct sm2_signature_ctx *sig = context;
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	if (!value || !vlen)
19862306a36Sopenharmony_ci		return -EINVAL;
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	sig->sig_r = mpi_read_raw_data(value, vlen);
20162306a36Sopenharmony_ci	if (!sig->sig_r)
20262306a36Sopenharmony_ci		return -ENOMEM;
20362306a36Sopenharmony_ci
20462306a36Sopenharmony_ci	return 0;
20562306a36Sopenharmony_ci}
20662306a36Sopenharmony_ci
20762306a36Sopenharmony_ciint sm2_get_signature_s(void *context, size_t hdrlen, unsigned char tag,
20862306a36Sopenharmony_ci				const void *value, size_t vlen)
20962306a36Sopenharmony_ci{
21062306a36Sopenharmony_ci	struct sm2_signature_ctx *sig = context;
21162306a36Sopenharmony_ci
21262306a36Sopenharmony_ci	if (!value || !vlen)
21362306a36Sopenharmony_ci		return -EINVAL;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	sig->sig_s = mpi_read_raw_data(value, vlen);
21662306a36Sopenharmony_ci	if (!sig->sig_s)
21762306a36Sopenharmony_ci		return -ENOMEM;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	return 0;
22062306a36Sopenharmony_ci}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic int sm2_z_digest_update(struct shash_desc *desc,
22362306a36Sopenharmony_ci			       MPI m, unsigned int pbytes)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	static const unsigned char zero[32];
22662306a36Sopenharmony_ci	unsigned char *in;
22762306a36Sopenharmony_ci	unsigned int inlen;
22862306a36Sopenharmony_ci	int err;
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	in = mpi_get_buffer(m, &inlen, NULL);
23162306a36Sopenharmony_ci	if (!in)
23262306a36Sopenharmony_ci		return -EINVAL;
23362306a36Sopenharmony_ci
23462306a36Sopenharmony_ci	if (inlen < pbytes) {
23562306a36Sopenharmony_ci		/* padding with zero */
23662306a36Sopenharmony_ci		err = crypto_shash_update(desc, zero, pbytes - inlen) ?:
23762306a36Sopenharmony_ci		      crypto_shash_update(desc, in, inlen);
23862306a36Sopenharmony_ci	} else if (inlen > pbytes) {
23962306a36Sopenharmony_ci		/* skip the starting zero */
24062306a36Sopenharmony_ci		err = crypto_shash_update(desc, in + inlen - pbytes, pbytes);
24162306a36Sopenharmony_ci	} else {
24262306a36Sopenharmony_ci		err = crypto_shash_update(desc, in, inlen);
24362306a36Sopenharmony_ci	}
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	kfree(in);
24662306a36Sopenharmony_ci	return err;
24762306a36Sopenharmony_ci}
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_cistatic int sm2_z_digest_update_point(struct shash_desc *desc,
25062306a36Sopenharmony_ci				     MPI_POINT point, struct mpi_ec_ctx *ec,
25162306a36Sopenharmony_ci				     unsigned int pbytes)
25262306a36Sopenharmony_ci{
25362306a36Sopenharmony_ci	MPI x, y;
25462306a36Sopenharmony_ci	int ret = -EINVAL;
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ci	x = mpi_new(0);
25762306a36Sopenharmony_ci	y = mpi_new(0);
25862306a36Sopenharmony_ci
25962306a36Sopenharmony_ci	ret = mpi_ec_get_affine(x, y, point, ec) ? -EINVAL :
26062306a36Sopenharmony_ci	      sm2_z_digest_update(desc, x, pbytes) ?:
26162306a36Sopenharmony_ci	      sm2_z_digest_update(desc, y, pbytes);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	mpi_free(x);
26462306a36Sopenharmony_ci	mpi_free(y);
26562306a36Sopenharmony_ci	return ret;
26662306a36Sopenharmony_ci}
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ciint sm2_compute_z_digest(struct shash_desc *desc,
26962306a36Sopenharmony_ci			 const void *key, unsigned int keylen, void *dgst)
27062306a36Sopenharmony_ci{
27162306a36Sopenharmony_ci	struct mpi_ec_ctx *ec;
27262306a36Sopenharmony_ci	unsigned int bits_len;
27362306a36Sopenharmony_ci	unsigned int pbytes;
27462306a36Sopenharmony_ci	u8 entl[2];
27562306a36Sopenharmony_ci	int err;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	ec = kmalloc(sizeof(*ec), GFP_KERNEL);
27862306a36Sopenharmony_ci	if (!ec)
27962306a36Sopenharmony_ci		return -ENOMEM;
28062306a36Sopenharmony_ci
28162306a36Sopenharmony_ci	err = sm2_ec_ctx_init(ec);
28262306a36Sopenharmony_ci	if (err)
28362306a36Sopenharmony_ci		goto out_free_ec;
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_ci	err = __sm2_set_pub_key(ec, key, keylen);
28662306a36Sopenharmony_ci	if (err)
28762306a36Sopenharmony_ci		goto out_deinit_ec;
28862306a36Sopenharmony_ci
28962306a36Sopenharmony_ci	bits_len = SM2_DEFAULT_USERID_LEN * 8;
29062306a36Sopenharmony_ci	entl[0] = bits_len >> 8;
29162306a36Sopenharmony_ci	entl[1] = bits_len & 0xff;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	pbytes = MPI_NBYTES(ec->p);
29462306a36Sopenharmony_ci
29562306a36Sopenharmony_ci	/* ZA = H256(ENTLA | IDA | a | b | xG | yG | xA | yA) */
29662306a36Sopenharmony_ci	err = crypto_shash_init(desc);
29762306a36Sopenharmony_ci	if (err)
29862306a36Sopenharmony_ci		goto out_deinit_ec;
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci	err = crypto_shash_update(desc, entl, 2);
30162306a36Sopenharmony_ci	if (err)
30262306a36Sopenharmony_ci		goto out_deinit_ec;
30362306a36Sopenharmony_ci
30462306a36Sopenharmony_ci	err = crypto_shash_update(desc, SM2_DEFAULT_USERID,
30562306a36Sopenharmony_ci				  SM2_DEFAULT_USERID_LEN);
30662306a36Sopenharmony_ci	if (err)
30762306a36Sopenharmony_ci		goto out_deinit_ec;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	err = sm2_z_digest_update(desc, ec->a, pbytes) ?:
31062306a36Sopenharmony_ci	      sm2_z_digest_update(desc, ec->b, pbytes) ?:
31162306a36Sopenharmony_ci	      sm2_z_digest_update_point(desc, ec->G, ec, pbytes) ?:
31262306a36Sopenharmony_ci	      sm2_z_digest_update_point(desc, ec->Q, ec, pbytes);
31362306a36Sopenharmony_ci	if (err)
31462306a36Sopenharmony_ci		goto out_deinit_ec;
31562306a36Sopenharmony_ci
31662306a36Sopenharmony_ci	err = crypto_shash_final(desc, dgst);
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ciout_deinit_ec:
31962306a36Sopenharmony_ci	sm2_ec_ctx_deinit(ec);
32062306a36Sopenharmony_ciout_free_ec:
32162306a36Sopenharmony_ci	kfree(ec);
32262306a36Sopenharmony_ci	return err;
32362306a36Sopenharmony_ci}
32462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(sm2_compute_z_digest);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_cistatic int _sm2_verify(struct mpi_ec_ctx *ec, MPI hash, MPI sig_r, MPI sig_s)
32762306a36Sopenharmony_ci{
32862306a36Sopenharmony_ci	int rc = -EINVAL;
32962306a36Sopenharmony_ci	struct gcry_mpi_point sG, tP;
33062306a36Sopenharmony_ci	MPI t = NULL;
33162306a36Sopenharmony_ci	MPI x1 = NULL, y1 = NULL;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	mpi_point_init(&sG);
33462306a36Sopenharmony_ci	mpi_point_init(&tP);
33562306a36Sopenharmony_ci	x1 = mpi_new(0);
33662306a36Sopenharmony_ci	y1 = mpi_new(0);
33762306a36Sopenharmony_ci	t = mpi_new(0);
33862306a36Sopenharmony_ci
33962306a36Sopenharmony_ci	/* r, s in [1, n-1] */
34062306a36Sopenharmony_ci	if (mpi_cmp_ui(sig_r, 1) < 0 || mpi_cmp(sig_r, ec->n) > 0 ||
34162306a36Sopenharmony_ci		mpi_cmp_ui(sig_s, 1) < 0 || mpi_cmp(sig_s, ec->n) > 0) {
34262306a36Sopenharmony_ci		goto leave;
34362306a36Sopenharmony_ci	}
34462306a36Sopenharmony_ci
34562306a36Sopenharmony_ci	/* t = (r + s) % n, t == 0 */
34662306a36Sopenharmony_ci	mpi_addm(t, sig_r, sig_s, ec->n);
34762306a36Sopenharmony_ci	if (mpi_cmp_ui(t, 0) == 0)
34862306a36Sopenharmony_ci		goto leave;
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	/* sG + tP = (x1, y1) */
35162306a36Sopenharmony_ci	rc = -EBADMSG;
35262306a36Sopenharmony_ci	mpi_ec_mul_point(&sG, sig_s, ec->G, ec);
35362306a36Sopenharmony_ci	mpi_ec_mul_point(&tP, t, ec->Q, ec);
35462306a36Sopenharmony_ci	mpi_ec_add_points(&sG, &sG, &tP, ec);
35562306a36Sopenharmony_ci	if (mpi_ec_get_affine(x1, y1, &sG, ec))
35662306a36Sopenharmony_ci		goto leave;
35762306a36Sopenharmony_ci
35862306a36Sopenharmony_ci	/* R = (e + x1) % n */
35962306a36Sopenharmony_ci	mpi_addm(t, hash, x1, ec->n);
36062306a36Sopenharmony_ci
36162306a36Sopenharmony_ci	/* check R == r */
36262306a36Sopenharmony_ci	rc = -EKEYREJECTED;
36362306a36Sopenharmony_ci	if (mpi_cmp(t, sig_r))
36462306a36Sopenharmony_ci		goto leave;
36562306a36Sopenharmony_ci
36662306a36Sopenharmony_ci	rc = 0;
36762306a36Sopenharmony_ci
36862306a36Sopenharmony_cileave:
36962306a36Sopenharmony_ci	mpi_point_free_parts(&sG);
37062306a36Sopenharmony_ci	mpi_point_free_parts(&tP);
37162306a36Sopenharmony_ci	mpi_free(x1);
37262306a36Sopenharmony_ci	mpi_free(y1);
37362306a36Sopenharmony_ci	mpi_free(t);
37462306a36Sopenharmony_ci
37562306a36Sopenharmony_ci	return rc;
37662306a36Sopenharmony_ci}
37762306a36Sopenharmony_ci
37862306a36Sopenharmony_cistatic int sm2_verify(struct akcipher_request *req)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct crypto_akcipher *tfm = crypto_akcipher_reqtfm(req);
38162306a36Sopenharmony_ci	struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);
38262306a36Sopenharmony_ci	unsigned char *buffer;
38362306a36Sopenharmony_ci	struct sm2_signature_ctx sig;
38462306a36Sopenharmony_ci	MPI hash;
38562306a36Sopenharmony_ci	int ret;
38662306a36Sopenharmony_ci
38762306a36Sopenharmony_ci	if (unlikely(!ec->Q))
38862306a36Sopenharmony_ci		return -EINVAL;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	buffer = kmalloc(req->src_len + req->dst_len, GFP_KERNEL);
39162306a36Sopenharmony_ci	if (!buffer)
39262306a36Sopenharmony_ci		return -ENOMEM;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	sg_pcopy_to_buffer(req->src,
39562306a36Sopenharmony_ci		sg_nents_for_len(req->src, req->src_len + req->dst_len),
39662306a36Sopenharmony_ci		buffer, req->src_len + req->dst_len, 0);
39762306a36Sopenharmony_ci
39862306a36Sopenharmony_ci	sig.sig_r = NULL;
39962306a36Sopenharmony_ci	sig.sig_s = NULL;
40062306a36Sopenharmony_ci	ret = asn1_ber_decoder(&sm2signature_decoder, &sig,
40162306a36Sopenharmony_ci				buffer, req->src_len);
40262306a36Sopenharmony_ci	if (ret)
40362306a36Sopenharmony_ci		goto error;
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	ret = -ENOMEM;
40662306a36Sopenharmony_ci	hash = mpi_read_raw_data(buffer + req->src_len, req->dst_len);
40762306a36Sopenharmony_ci	if (!hash)
40862306a36Sopenharmony_ci		goto error;
40962306a36Sopenharmony_ci
41062306a36Sopenharmony_ci	ret = _sm2_verify(ec, hash, sig.sig_r, sig.sig_s);
41162306a36Sopenharmony_ci
41262306a36Sopenharmony_ci	mpi_free(hash);
41362306a36Sopenharmony_cierror:
41462306a36Sopenharmony_ci	mpi_free(sig.sig_r);
41562306a36Sopenharmony_ci	mpi_free(sig.sig_s);
41662306a36Sopenharmony_ci	kfree(buffer);
41762306a36Sopenharmony_ci	return ret;
41862306a36Sopenharmony_ci}
41962306a36Sopenharmony_ci
42062306a36Sopenharmony_cistatic int sm2_set_pub_key(struct crypto_akcipher *tfm,
42162306a36Sopenharmony_ci			const void *key, unsigned int keylen)
42262306a36Sopenharmony_ci{
42362306a36Sopenharmony_ci	struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	return __sm2_set_pub_key(ec, key, keylen);
42662306a36Sopenharmony_ci
42762306a36Sopenharmony_ci}
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_cistatic int __sm2_set_pub_key(struct mpi_ec_ctx *ec,
43062306a36Sopenharmony_ci			     const void *key, unsigned int keylen)
43162306a36Sopenharmony_ci{
43262306a36Sopenharmony_ci	MPI a;
43362306a36Sopenharmony_ci	int rc;
43462306a36Sopenharmony_ci
43562306a36Sopenharmony_ci	/* include the uncompressed flag '0x04' */
43662306a36Sopenharmony_ci	a = mpi_read_raw_data(key, keylen);
43762306a36Sopenharmony_ci	if (!a)
43862306a36Sopenharmony_ci		return -ENOMEM;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_ci	mpi_normalize(a);
44162306a36Sopenharmony_ci	rc = sm2_ecc_os2ec(ec->Q, a);
44262306a36Sopenharmony_ci	mpi_free(a);
44362306a36Sopenharmony_ci
44462306a36Sopenharmony_ci	return rc;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic unsigned int sm2_max_size(struct crypto_akcipher *tfm)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	/* Unlimited max size */
45062306a36Sopenharmony_ci	return PAGE_SIZE;
45162306a36Sopenharmony_ci}
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_cistatic int sm2_init_tfm(struct crypto_akcipher *tfm)
45462306a36Sopenharmony_ci{
45562306a36Sopenharmony_ci	struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return sm2_ec_ctx_init(ec);
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic void sm2_exit_tfm(struct crypto_akcipher *tfm)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct mpi_ec_ctx *ec = akcipher_tfm_ctx(tfm);
46362306a36Sopenharmony_ci
46462306a36Sopenharmony_ci	sm2_ec_ctx_deinit(ec);
46562306a36Sopenharmony_ci}
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_cistatic struct akcipher_alg sm2 = {
46862306a36Sopenharmony_ci	.verify = sm2_verify,
46962306a36Sopenharmony_ci	.set_pub_key = sm2_set_pub_key,
47062306a36Sopenharmony_ci	.max_size = sm2_max_size,
47162306a36Sopenharmony_ci	.init = sm2_init_tfm,
47262306a36Sopenharmony_ci	.exit = sm2_exit_tfm,
47362306a36Sopenharmony_ci	.base = {
47462306a36Sopenharmony_ci		.cra_name = "sm2",
47562306a36Sopenharmony_ci		.cra_driver_name = "sm2-generic",
47662306a36Sopenharmony_ci		.cra_priority = 100,
47762306a36Sopenharmony_ci		.cra_module = THIS_MODULE,
47862306a36Sopenharmony_ci		.cra_ctxsize = sizeof(struct mpi_ec_ctx),
47962306a36Sopenharmony_ci	},
48062306a36Sopenharmony_ci};
48162306a36Sopenharmony_ci
48262306a36Sopenharmony_cistatic int __init sm2_init(void)
48362306a36Sopenharmony_ci{
48462306a36Sopenharmony_ci	return crypto_register_akcipher(&sm2);
48562306a36Sopenharmony_ci}
48662306a36Sopenharmony_ci
48762306a36Sopenharmony_cistatic void __exit sm2_exit(void)
48862306a36Sopenharmony_ci{
48962306a36Sopenharmony_ci	crypto_unregister_akcipher(&sm2);
49062306a36Sopenharmony_ci}
49162306a36Sopenharmony_ci
49262306a36Sopenharmony_cisubsys_initcall(sm2_init);
49362306a36Sopenharmony_cimodule_exit(sm2_exit);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
49662306a36Sopenharmony_ciMODULE_AUTHOR("Tianjia Zhang <tianjia.zhang@linux.alibaba.com>");
49762306a36Sopenharmony_ciMODULE_DESCRIPTION("SM2 generic algorithm");
49862306a36Sopenharmony_ciMODULE_ALIAS_CRYPTO("sm2-generic");
499