diff mbox series

[RFC,2/3] crypto: Introduce SM9 key exchange algorithm library

Message ID 20230617101443.6083-3-guozihua@huawei.com (mailing list archive)
State Superseded
Delegated to: Herbert Xu
Headers show
Series crypto: Introduce SM9 key exchange | expand

Commit Message

Guozihua (Scott) June 17, 2023, 10:14 a.m. UTC
This commit introduces library for SM9 (ShangMi9) key exchange crypto
algorithm.

SM9 is an ID-based crypto algorithm providing asymmetric encryption,
signature and key exchange capability. Within which, the key exchange
algorithm has been accepted in ISO/IEC 11770-3:2021 international
standard.

sm9_lib.c contains mathematical algorithms used by SM9 key exchange
algorithm.

References:
http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=B7A0D7DFF411CD0AAE76135ADE91886A
http://c.gb688.cn/bzgk/gb/showGb?type=online&hcno=02A8E87248BD500747D2CD484C034EB0
https://github.com/guanzhi/GmSSL

Co-developed-by: LI Shiya <lishiya1@huawei.com>
Signed-off-by: GUO Zihua <guozihua@huawei.com>
---
 crypto/sm9_lib.c | 1584 ++++++++++++++++++++++++++++++++++++++++++++++
 crypto/sm9_lib.h |   92 +++
 2 files changed, 1676 insertions(+)
 create mode 100644 crypto/sm9_lib.c
 create mode 100644 crypto/sm9_lib.h
diff mbox series

Patch

diff --git a/crypto/sm9_lib.c b/crypto/sm9_lib.c
new file mode 100644
index 000000000000..7f8545c9e640
--- /dev/null
+++ b/crypto/sm9_lib.c
@@ -0,0 +1,1584 @@ 
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Libraries for SM9 key exchange algorithm
+ *
+ * Copyright (c) 2023, Huawei Technology Co., Ltd.
+ * Authors: GUO Zihua <guozihua@huawei.com>
+ */
+
+#include <linux/module.h>
+#include <linux/mpi.h>
+#include <linux/string.h>
+#include <crypto/sm9.h>
+
+#include "sm9_lib.h"
+
+unsigned int mpi_dump_to_buf(MPI a, u8 *buf, unsigned int expected_size)
+{
+	unsigned int written_size;
+	u8 *mpi_buf;
+
+	if (expected_size < mpi_get_size(a))
+		return 0;
+
+	mpi_buf = mpi_get_buffer(a, &written_size, NULL);
+	if (!mpi_buf)
+		return 0;
+	memcpy(buf + (expected_size - written_size), mpi_buf, expected_size);
+	kfree(mpi_buf);
+	return expected_size;
+}
+
+int mpi_point_jacobian_to_affine(MPI_POINT out, MPI_POINT p, MPI q)
+{
+	MPI z, _q;
+	int rc = -ENOMEM;
+
+	if (!out)
+		return -EINVAL;
+
+	z = mpi_new(0);
+	_q = mpi_new(0);
+	if (!z || !_q)
+		goto out_free;
+
+	mpi_sub_ui(_q, q, 3);
+	mpi_powm(z, p->z, _q, q);
+	mpi_mulm(out->x, p->x, z, q);
+
+	mpi_sub_ui(_q, q, 4);
+	mpi_powm(z, p->z, _q, q);
+	mpi_mulm(out->y, p->y, z, q);
+
+	mpi_set_ui(out->z, 1);
+	rc = 0;
+
+out_free:
+	mpi_free(z);
+	mpi_free(_q);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(mpi_point_jacobian_to_affine);
+
+int mpi_point_export(MPI_POINT P, u8 *buf, size_t size)
+{
+	unsigned int nbytes, x_size;
+	int rc;
+
+	if (size < max(mpi_get_size(P->x), mpi_get_size(P->y)) * 2)
+		return -EINVAL;
+
+	x_size = size / 2;
+	rc = mpi_read_buffer(P->x, buf, x_size, &nbytes, NULL);
+	if (rc)
+		return nbytes;
+
+	rc = mpi_read_buffer(P->y, buf + x_size, x_size, &nbytes, NULL);
+	if (rc)
+		return nbytes;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_point_export);
+
+int mpi_point_from_buf(MPI_POINT P, const u8 *buf, size_t size)
+{
+	size_t x_size;
+	MPI x, y;
+
+	if (size % 2)
+		return -EINVAL;
+
+	x_size = size / 2;
+	x = mpi_read_raw_data(buf, x_size);
+	y = mpi_read_raw_data(buf + x_size, x_size);
+	if (!x || !y) {
+		mpi_free(x);
+		mpi_free(y);
+		return -ENOMEM;
+	}
+
+	mpi_set(P->x, x);
+	mpi_set(P->y, y);
+	mpi_set_ui(P->z, 1);
+
+	mpi_free(x);
+	mpi_free(y);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_point_from_buf);
+
+static void mpi_div2m(MPI out, MPI a, MPI q)
+{
+	mpi_set(out, a);
+
+	if (mpi_test_bit(a, 0))
+		mpi_add(out, out, q);
+	mpi_rshift(out, out, 1);
+	mpi_mod(out, out, q);
+}
+
+static bool sm9_dim_eq(SM9_DIM_FQ2 a, SM9_DIM_FQ2 b)
+{
+	return !(mpi_cmp(a[0], b[0]) && mpi_cmp(a[1], b[1]));
+}
+
+int sm9_dim_init(SM9_DIM_FQ2 dim, unsigned int nbits)
+{
+	if (!dim)
+		return -EINVAL;
+
+	dim[0] = mpi_new(nbits);
+	dim[1] = mpi_new(nbits);
+	if (!dim[0] || !dim[1])
+		return -ENOMEM;
+	return 0;
+}
+
+static int sm9_dim_init_from_buf(SM9_DIM_FQ2 dim, const u8 *buf, size_t size)
+{
+	size_t d_size;
+
+	if (size % 2)
+		return -EINVAL;
+
+	d_size = size / 2;
+	dim[1] = mpi_read_raw_data(buf, d_size);
+	dim[0] = mpi_read_raw_data(buf + d_size, d_size);
+	return 0;
+}
+
+void sm9_dim_deinit(SM9_DIM_FQ2 dim)
+{
+	mpi_free(dim[0]);
+	mpi_free(dim[1]);
+}
+
+void sm9_dim_free(SM9_DIM_FQ2 *dim)
+{
+	if (!dim)
+		return;
+
+	sm9_dim_deinit(*dim);
+	kfree(dim);
+}
+
+SM9_DIM_FQ2 *sm9_dim_alloc(unsigned int nbits)
+{
+	SM9_DIM_FQ2 *res;
+
+	res = kzalloc(sizeof(SM9_DIM_FQ2), GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	if (sm9_dim_init(*res, nbits)) {
+		kfree(res);
+		res = NULL;
+	}
+	return res;
+}
+
+int sm9_dim_set(SM9_DIM_FQ2 a, SM9_DIM_FQ2 b)
+{
+	mpi_set(a[0], b[0]);
+	mpi_set(a[1], b[1]);
+	return 0;
+}
+
+int sm9_dim_clear(SM9_DIM_FQ2 a)
+{
+	memzero_explicit(a[0]->d, a[0]->alloced * BYTES_PER_MPI_LIMB);
+	mpi_clear(a[0]);
+	memzero_explicit(a[1]->d, a[1]->alloced * BYTES_PER_MPI_LIMB);
+	mpi_clear(a[1]);
+	return 0;
+}
+
+static int sm9_dim_mulm(SM9_DIM_FQ2 out, SM9_DIM_FQ2 _a, SM9_DIM_FQ2 _b, MPI q)
+{
+	MPI tmp;
+	SM9_DIM_FQ2 a, b;
+	int rc;
+
+	if (!out)
+		return -EINVAL;
+
+	tmp = mpi_new(0);
+	if (!tmp)
+		return -ENOMEM;
+
+	rc = sm9_dim_init(a, 0);
+	rc |= sm9_dim_init(b, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_set(a, _a);
+	sm9_dim_set(b, _b);
+
+	mpi_mulm(out[0], a[0], b[0], q);
+	mpi_mulm(tmp, a[1], b[1], q);
+	mpi_addm(tmp, tmp, tmp, q);
+	mpi_subm(out[0], out[0], tmp, q);
+
+	mpi_mulm(out[1], a[0], b[1], q);
+	mpi_mulm(tmp, a[1], b[0], q);
+	mpi_addm(out[1], out[1], tmp, q);
+
+out_free:
+	sm9_dim_deinit(a);
+	sm9_dim_deinit(b);
+	mpi_free(tmp);
+	return rc;
+}
+
+static int sm9_dim_mulm_u(SM9_DIM_FQ2 out, SM9_DIM_FQ2 _a, SM9_DIM_FQ2 _b,
+			  MPI q)
+{
+	MPI tmp;
+	SM9_DIM_FQ2 a, b;
+	int rc;
+
+	if (!out)
+		return -EINVAL;
+
+	tmp = mpi_new(0);
+	if (!tmp)
+		return -ENOMEM;
+
+	rc = sm9_dim_init(a, 0);
+	rc |= sm9_dim_init(b, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_set(a, _a);
+	sm9_dim_set(b, _b);
+
+	mpi_mulm(out[0], a[0], b[1], q);
+	mpi_mulm(tmp, a[1], b[0], q);
+	mpi_addm(out[0], out[0], tmp, q);
+	mpi_addm(out[0], out[0], out[0], q);
+	mpi_subm(out[0], q, out[0], q);
+
+	mpi_mulm(out[1], a[0], b[0], q);
+	mpi_mulm(tmp, a[1], b[1], q);
+	mpi_addm(tmp, tmp, tmp, q);
+	mpi_subm(out[1], out[1], tmp, q);
+
+out_free:
+	sm9_dim_deinit(a);
+	sm9_dim_deinit(b);
+	mpi_free(tmp);
+	return 0;
+}
+
+static int sm9_dim_mulm_mpi(SM9_DIM_FQ2 out, SM9_DIM_FQ2 P, MPI a, MPI q)
+{
+	mpi_mulm(out[0], P[0], a, q);
+	mpi_mulm(out[1], P[1], a, q);
+	return 0;
+}
+
+static int sm9_dim_mulm_ui(SM9_DIM_FQ2 out, SM9_DIM_FQ2 P, unsigned int a,
+			   MPI q)
+{
+	int rc;
+	MPI p = mpi_new(0);
+
+	if (!p)
+		return -ENOMEM;
+
+	mpi_set_ui(p, a);
+	rc = sm9_dim_mulm_mpi(out, P, p, q);
+	mpi_free(p);
+	return rc;
+}
+
+static int sm9_dim_subm(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, SM9_DIM_FQ2 b, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_subm(out[0], a[0], b[0], q);
+	mpi_subm(out[1], a[1], b[1], q);
+	return 0;
+}
+
+static int sm9_dim_addm(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, SM9_DIM_FQ2 b, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_addm(out[0], a[0], b[0], q);
+	mpi_addm(out[1], a[1], b[1], q);
+	return 0;
+}
+
+static int sm9_dim_negm(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_subm(out[0], q, a[0], q);
+	mpi_subm(out[1], q, a[1], q);
+	return 0;
+}
+
+static int sm9_dim_div2m(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_div2m(out[0], a[0], q);
+	mpi_div2m(out[1], a[1], q);
+	return 0;
+}
+
+static int sm9_dim_conjugate(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_set(out[0], a[0]);
+	mpi_subm(out[1], q, a[1], q);
+	return 0;
+}
+
+static int sm9_dim_copy(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_set(out[0], a[0]);
+	mpi_set(out[1], a[1]);
+	return 0;
+}
+
+static int sm9_dim_is_zero(SM9_DIM_FQ2 a)
+{
+	return !(mpi_cmp_ui(a[0], 0) || mpi_cmp_ui(a[1], 0));
+}
+
+static int sm9_dim_invm(SM9_DIM_FQ2 out, SM9_DIM_FQ2 a, MPI q, MPI q_minus_2)
+{
+	MPI t0, t1;
+
+	if (!out)
+		return -EINVAL;
+
+	if (!mpi_cmp_ui(a[0], 0)) {
+		mpi_clear(out[0]);
+		mpi_addm(out[1], a[1], a[1], q);
+		mpi_powm(out[1], out[1], q_minus_2, q);
+		mpi_subm(out[1], q, out[1], q);
+	} else if (!mpi_cmp_ui(a[1], 0)) {
+		mpi_clear(out[1]);
+		mpi_powm(out[0], q_minus_2, a[0], q);
+	} else {
+		t0 = mpi_new(0);
+		t1 = mpi_new(0);
+		if (!t0 || !t1) {
+			mpi_free(t0);
+			mpi_free(t1);
+			return -ENOMEM;
+		}
+
+		mpi_mulm(t0, a[0], a[0], q);
+		mpi_mulm(t1, a[1], a[1], q);
+		mpi_addm(t1, t1, t1, q);
+		mpi_addm(t0, t0, t1, q);
+		mpi_powm(t0, t0, q_minus_2, q);
+
+		mpi_mulm(out[0], a[0], t0, q);
+		mpi_mulm(out[1], a[1], t0, q);
+		mpi_subm(out[1], q, out[1], q);
+
+		mpi_free(t0);
+		mpi_free(t1);
+	}
+	return 0;
+}
+
+static size_t sm9_dim_get_size(SM9_DIM_FQ2 a)
+{
+	size_t size;
+
+	size = mpi_get_size(a[0]);
+	size += mpi_get_size(a[1]);
+	return size;
+}
+
+static ssize_t sm9_dim_to_buf_rev(SM9_DIM_FQ2 a, char *buf, size_t size)
+{
+	unsigned int d_size = size / 2, written_size;
+
+	written_size = mpi_dump_to_buf(a[1], buf, d_size);
+	if (written_size != d_size)
+		return -ENOMEM;
+
+	written_size = mpi_dump_to_buf(a[0], buf + d_size, d_size);
+	if (written_size != d_size)
+		return -ENOMEM;
+	return size;
+}
+
+static int sm9_point_copy(SM9_POINT out, SM9_POINT p)
+{
+	if (!out)
+		return -EINVAL;
+
+	if (out == p)
+		return 0;
+
+	mpi_set(out->xd1, p->xd1);
+	mpi_set(out->xd2, p->xd2);
+	mpi_set(out->yd1, p->yd1);
+	mpi_set(out->yd2, p->yd2);
+	mpi_set(out->zd1, p->zd1);
+	mpi_set(out->zd2, p->zd2);
+	return 0;
+}
+
+void sm9_point_release(SM9_POINT P)
+{
+	if (!P)
+		return;
+
+	sm9_dim_deinit(P->x_fq2);
+	sm9_dim_deinit(P->y_fq2);
+	sm9_dim_deinit(P->z_fq2);
+	kfree(P);
+}
+
+SM9_POINT sm9_point_new(unsigned int nbits)
+{
+	SM9_POINT res;
+
+	res = kzalloc(sizeof(struct sm9_point_fq2), GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	if (sm9_dim_init(res->x_fq2, nbits) ||
+	    sm9_dim_init(res->y_fq2, nbits) ||
+	    sm9_dim_init(res->z_fq2, nbits)) {
+		sm9_point_release(res);
+		return NULL;
+	}
+	return res;
+}
+
+SM9_POINT sm9_point_from_buf(const u8 *buf, size_t size)
+{
+	SM9_POINT res;
+	size_t x_size;
+	int rc;
+
+	res = kzalloc(sizeof(struct sm9_point_fq2), GFP_KERNEL);
+	if (!res)
+		return NULL;
+
+	x_size = size / 2;
+	rc = sm9_dim_init_from_buf(res->x_fq2, buf, x_size);
+	if (rc) {
+		kfree(res);
+		return NULL;
+	}
+
+	rc = sm9_dim_init_from_buf(res->y_fq2, buf + x_size, x_size);
+	if (rc) {
+		sm9_dim_deinit(res->x_fq2);
+		kfree(res);
+		return NULL;
+	}
+
+	rc = sm9_dim_init(res->z_fq2, 0);
+	if (rc) {
+		sm9_dim_deinit(res->x_fq2);
+		sm9_dim_deinit(res->y_fq2);
+		kfree(res);
+	}
+	mpi_set_ui(res->zd1, 1);
+	return res;
+}
+EXPORT_SYMBOL_GPL(sm9_point_from_buf);
+
+static bool sm9_point_is_infinity(SM9_POINT p)
+{
+	return !(mpi_cmp_ui(p->zd1, 0) && mpi_cmp_ui(p->zd2, 0));
+}
+
+bool sm9_point_valid(SM9_POINT P, struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 x, y;
+	SM9_POINT Q;
+	MPI q = ctx->sys_cfg->q;
+	bool rc;
+
+	if (sm9_dim_init(x, 0) || sm9_dim_init(y, 0)) {
+		sm9_dim_deinit(x);
+		sm9_dim_deinit(y);
+		return false;
+	}
+
+	sm9_dim_mulm(x, P->x_fq2, P->x_fq2, q);
+	sm9_dim_mulm(x, x, P->x_fq2, q);
+	mpi_addm(x[0], x[0], ctx->sys_cfg->b, q);
+
+	sm9_dim_mulm(y, P->y_fq2, P->y_fq2, q);
+	if (!sm9_dim_eq(x, y)) {
+		sm9_dim_deinit(x);
+		sm9_dim_deinit(y);
+		return false;
+	}
+
+	Q = sm9_point_new(0);
+	if (!Q) {
+		sm9_dim_deinit(x);
+		sm9_dim_deinit(y);
+		return false;
+	}
+	sm9_point_mpi_mulm(Q, P, ctx->sys_cfg->q, ctx);
+	rc = sm9_point_is_infinity(Q);
+
+	sm9_dim_deinit(x);
+	sm9_dim_deinit(y);
+	sm9_point_release(Q);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(sm9_point_valid);
+
+static int sm9_point_addm_same(SM9_POINT out, SM9_POINT _P, struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 x2, x4, y2, y4, tmp;
+	SM9_POINT P;
+	MPI q = ctx->sys_cfg->q;
+	int rc;
+
+	rc = sm9_dim_init(x2, 0);
+	rc |= sm9_dim_init(x4, 0);
+	rc |= sm9_dim_init(y2, 0);
+	rc |= sm9_dim_init(y4, 0);
+	rc |= sm9_dim_init(tmp, 0);
+	if (rc)
+		goto out_free;
+
+	P = sm9_point_new(0);
+	if (!P)
+		goto out_free;
+	sm9_point_copy(P, _P);
+
+	sm9_dim_mulm(x2, P->x_fq2, P->x_fq2, q);
+	sm9_dim_mulm(x4, x2, x2, q);
+	sm9_dim_mulm(y2, P->y_fq2, P->y_fq2, q);
+	sm9_dim_mulm(y4, y2, y2, q);
+
+	/* x3 = 9 * x1^4 - 8 * x1 * y1^2 */
+	sm9_dim_mulm_ui(out->x_fq2, x4, 9, q);
+	sm9_dim_mulm(tmp, P->x_fq2, y2, q);
+	sm9_dim_mulm_ui(tmp, tmp, 8, q);
+	sm9_dim_subm(out->x_fq2, out->x_fq2, tmp, q);
+
+	/* y3 = 3 * x1^2 * (4 * x1 * y1^2 - x3) - 8 * y1^4 */
+	sm9_dim_mulm(out->y_fq2, P->x_fq2, y2, q);
+	sm9_dim_mulm_ui(out->y_fq2, out->y_fq2, 4, q);
+	sm9_dim_subm(out->y_fq2, out->y_fq2, out->x_fq2, q);
+	sm9_dim_mulm(out->y_fq2, out->y_fq2, x2, q);
+	sm9_dim_mulm_ui(out->y_fq2, out->y_fq2, 3, q);
+	sm9_dim_mulm_ui(tmp, y4, 8, q);
+	sm9_dim_subm(out->y_fq2, out->y_fq2, tmp, q);
+
+	/* z3 = 2 * y1 * z1 */
+	sm9_dim_mulm(out->z_fq2, P->y_fq2, P->z_fq2, q);
+	sm9_dim_mulm_ui(out->z_fq2, out->z_fq2, 2, q);
+
+	rc = 0;
+out_free:
+	sm9_dim_deinit(x2);
+	sm9_dim_deinit(x4);
+	sm9_dim_deinit(y2);
+	sm9_dim_deinit(y4);
+	sm9_dim_deinit(tmp);
+	sm9_point_release(P);
+	return rc;
+}
+
+static int sm9_point_addm_diff(SM9_POINT out, SM9_POINT _P, SM9_POINT _Q,
+			       struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 u1, u2, s1, s2, h, h2, h3, r, tmp;
+	SM9_POINT P, Q;
+	MPI q = ctx->sys_cfg->q;
+	int rc;
+
+	rc = sm9_dim_init(u1, 0);
+	rc |= sm9_dim_init(u2, 0);
+	rc |= sm9_dim_init(s1, 0);
+	rc |= sm9_dim_init(s2, 0);
+	rc |= sm9_dim_init(h, 0);
+	rc |= sm9_dim_init(h2, 0);
+	rc |= sm9_dim_init(h3, 0);
+	rc |= sm9_dim_init(r, 0);
+	rc |= sm9_dim_init(tmp, 0);
+	if (rc)
+		goto out_free;
+
+	P = sm9_point_new(0);
+	Q = sm9_point_new(0);
+	if (!Q || !P)
+		goto out_free_point;
+	sm9_point_copy(P, _P);
+	sm9_point_copy(Q, _Q);
+
+	/* u1 = x1 * z2^2 */
+	sm9_dim_mulm(u1, Q->z_fq2, Q->z_fq2, q);
+	sm9_dim_mulm(u1, P->x_fq2, u1, q);
+	/* u2 = x2 * z1^2 */
+	sm9_dim_mulm(u2, P->z_fq2, P->z_fq2, q);
+	sm9_dim_mulm(u2, Q->x_fq2, u2, q);
+	/* s1 = y1 * z2^3 */
+	sm9_dim_mulm(s1, Q->z_fq2, Q->z_fq2, q);
+	sm9_dim_mulm(s1, s1, Q->z_fq2, q);
+	sm9_dim_mulm(s1, P->y_fq2, s1, q);
+	/* s2 = y2 * z1^3 */
+	sm9_dim_mulm(s2, P->z_fq2, P->z_fq2, q);
+	sm9_dim_mulm(s2, s2, P->z_fq2, q);
+	sm9_dim_mulm(s2, Q->y_fq2, s2, q);
+	/* h = u2 - u1 */
+	sm9_dim_subm(h, u2, u1, q);
+	/* r = s2 - s1 */
+	sm9_dim_subm(r, s2, s1, q);
+
+	/* x3 = r^2 - h^3 - 2 * u1 * h^2 */
+	sm9_dim_mulm(out->x_fq2, r, r, q);
+	sm9_dim_mulm(out->x_fq2, r, r, q);
+	sm9_dim_mulm(h2, h, h, q);
+	sm9_dim_mulm(h3, h2, h, q);
+	sm9_dim_subm(out->x_fq2, out->x_fq2, h3, q);
+	sm9_dim_mulm(tmp, u1, h2, q);
+	sm9_dim_mulm_ui(tmp, tmp, 2, q);
+	sm9_dim_subm(out->x_fq2, out->x_fq2, tmp, q);
+
+	/* y3 = r * (u1 * h^2 - x3) - s1 * h^3 */
+	sm9_dim_mulm(out->y_fq2, u1, h2, q);
+	sm9_dim_subm(out->y_fq2, out->y_fq2, out->x_fq2, q);
+	sm9_dim_mulm(out->y_fq2, r, out->y_fq2, q);
+	sm9_dim_mulm(tmp, s1, h3, q);
+	sm9_dim_subm(out->y_fq2, out->y_fq2, tmp, q);
+
+	/* z3 = z1 * z2 * h */
+	sm9_dim_mulm(out->z_fq2, P->z_fq2, Q->z_fq2, q);
+	sm9_dim_mulm(out->z_fq2, out->z_fq2, h, q);
+
+	rc = 0;
+out_free_point:
+	sm9_point_release(P);
+	sm9_point_release(Q);
+out_free:
+	sm9_dim_deinit(u1);
+	sm9_dim_deinit(u2);
+	sm9_dim_deinit(s1);
+	sm9_dim_deinit(s2);
+	sm9_dim_deinit(h);
+	sm9_dim_deinit(h2);
+	sm9_dim_deinit(h3);
+	sm9_dim_deinit(r);
+	sm9_dim_deinit(tmp);
+	return rc;
+}
+
+static int sm9_point_same(SM9_POINT a, SM9_POINT b)
+{
+	if (a == b)
+		return 1;
+
+	if (!mpi_cmp(a->xd1, b->xd1) && !mpi_cmp(a->xd2, b->xd2) &&
+	    !mpi_cmp(a->yd1, b->yd1) && !mpi_cmp(a->yd2, b->yd2) &&
+	    !mpi_cmp(a->zd1, b->zd1) && !mpi_cmp(a->zd2, b->zd2))
+		return 1;
+	return 0;
+}
+
+int sm9_point_addm(SM9_POINT out, SM9_POINT P, SM9_POINT Q, struct sm9_ctx *ctx)
+{
+	if (!out)
+		return -EINVAL;
+
+	if (sm9_dim_is_zero(P->z_fq2)) {
+		sm9_point_copy(out, Q);
+		return 0;
+	} else if (sm9_dim_is_zero(Q->z_fq2)) {
+		sm9_point_copy(out, P);
+		return 0;
+	}
+
+	if (sm9_point_same(P, Q))
+		return sm9_point_addm_same(out, P, ctx);
+	return sm9_point_addm_diff(out, P, Q, ctx);
+}
+EXPORT_SYMBOL_GPL(sm9_point_addm);
+
+int sm9_point_mpi_mulm(SM9_POINT out, SM9_POINT _P, MPI a, struct sm9_ctx *ctx)
+{
+	SM9_POINT P;
+	int i;
+
+	if (!out)
+		return -EINVAL;
+
+	P = sm9_point_new(0);
+	if (!P)
+		return -ENOMEM;
+	mpi_set_ui(P->xd1, 1);
+	mpi_set_ui(P->yd1, 1);
+	mpi_set_ui(P->zd1, 0);
+
+	for (i = mpi_get_nbits(a) - 1; i >= 0; i--) {
+		sm9_point_addm(P, P, P, ctx);
+		if (mpi_test_bit(a, i))
+			sm9_point_addm(P, P, _P, ctx);
+	}
+	sm9_point_copy(out, P);
+	sm9_point_release(P);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sm9_point_mpi_mulm);
+
+int sm9_point_mpi_addm(SM9_POINT out, SM9_POINT P, MPI a, MPI q)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_addm(out->xd1, P->xd1, a, q);
+	mpi_addm(out->yd1, P->yd1, a, q);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sm9_point_mpi_addm);
+
+void sm9_point_clear(SM9_POINT p)
+{
+	sm9_dim_clear(p->x_fq2);
+	sm9_dim_clear(p->y_fq2);
+	sm9_dim_clear(p->z_fq2);
+}
+EXPORT_SYMBOL_GPL(sm9_point_clear);
+
+int sm9_dim_fq4_set(SM9_DIM_FQ4 a, SM9_DIM_FQ4 b)
+{
+	sm9_dim_set(a[0], b[0]);
+	sm9_dim_set(a[1], b[1]);
+	return 0;
+}
+
+void sm9_dim_fq4_deinit(SM9_DIM_FQ4 p)
+{
+	sm9_dim_deinit(p[0]);
+	sm9_dim_deinit(p[1]);
+}
+
+int sm9_dim_fq4_init(SM9_DIM_FQ4 p, unsigned int nbits)
+{
+	int i, rc;
+
+	if (!p)
+		return -EINVAL;
+
+	for (i = 0; i < 2; i++) {
+		rc = sm9_dim_init(p[i], 0);
+		if (rc) {
+			sm9_dim_fq4_deinit(p);
+			return rc;
+		}
+	}
+	return 0;
+}
+
+int sm9_dim_fq4_clear(SM9_DIM_FQ4 p)
+{
+	sm9_dim_clear(p[0]);
+	sm9_dim_clear(p[1]);
+	return 0;
+}
+
+static int sm9_dim_fq4_mulm(SM9_DIM_FQ4 out, SM9_DIM_FQ4 _a, SM9_DIM_FQ4 _b,
+			    struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 tmp;
+	SM9_DIM_FQ4 a, b;
+	MPI q = ctx->sys_cfg->q;
+	int rc;
+
+	rc = sm9_dim_init(tmp, 0);
+	rc |= sm9_dim_fq4_init(a, 0);
+	rc |= sm9_dim_fq4_init(b, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_fq4_set(a, _a);
+	sm9_dim_fq4_set(b, _b);
+
+	sm9_dim_mulm(out[0], a[0], b[0], q);
+	sm9_dim_mulm_u(tmp, a[1], b[1], q);
+	sm9_dim_addm(out[0], out[0], tmp, q);
+
+	sm9_dim_mulm(out[1], a[0], b[1], q);
+	sm9_dim_mulm(tmp, a[1], b[0], q);
+	sm9_dim_addm(out[1], out[1], tmp, q);
+
+out_free:
+	sm9_dim_fq4_deinit(a);
+	sm9_dim_fq4_deinit(b);
+	sm9_dim_deinit(tmp);
+	return rc;
+}
+
+static int sm9_dim_fq4_mulm_mpi(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a, MPI b,
+				struct sm9_ctx *ctx)
+{
+	sm9_dim_mulm_mpi(out[0], a[0], b, ctx->sys_cfg->q);
+	sm9_dim_mulm_mpi(out[1], a[1], b, ctx->sys_cfg->q);
+	return 0;
+}
+
+static int sm9_dim_fq4_mulm_v(SM9_DIM_FQ4 out, SM9_DIM_FQ4 _a, SM9_DIM_FQ4 _b,
+			      struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 tmp;
+	SM9_DIM_FQ4 a, b;
+	MPI q = ctx->sys_cfg->q;
+	int rc;
+
+	rc = sm9_dim_init(tmp, 0);
+	rc |= sm9_dim_fq4_init(a, 0);
+	rc |= sm9_dim_fq4_init(b, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_fq4_set(a, _a);
+	sm9_dim_fq4_set(b, _b);
+
+	sm9_dim_mulm_u(out[0], a[1], b[0], q);
+	sm9_dim_mulm_u(tmp, a[0], b[1], q);
+	sm9_dim_addm(out[0], out[0], tmp, q);
+
+	sm9_dim_mulm(out[1], a[0], b[0], q);
+	sm9_dim_mulm_u(tmp, a[1], b[1], q);
+	sm9_dim_addm(out[1], out[1], tmp, q);
+
+out_free:
+	sm9_dim_fq4_deinit(a);
+	sm9_dim_fq4_deinit(b);
+	sm9_dim_deinit(tmp);
+	return 0;
+}
+
+static int sm9_dim_fq4_addm(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a, SM9_DIM_FQ4 b,
+			    struct sm9_ctx *ctx)
+{
+	MPI q = ctx->sys_cfg->q;
+
+	sm9_dim_addm(out[0], a[0], b[0], q);
+	sm9_dim_addm(out[1], a[1], b[1], q);
+
+	return 0;
+}
+
+static int sm9_dim_fq4_subm(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a, SM9_DIM_FQ4 b,
+			    struct sm9_ctx *ctx)
+{
+	sm9_dim_subm(out[0], a[0], b[0], ctx->sys_cfg->q);
+	sm9_dim_subm(out[1], a[1], b[1], ctx->sys_cfg->q);
+	return 0;
+}
+
+static int sm9_dim_fq4_invm(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a, struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 t0, t1, t2;
+	MPI q = ctx->sys_cfg->q;
+
+	if (sm9_dim_init(t0, 0) || sm9_dim_init(t1, 0) || sm9_dim_init(t2, 0)) {
+		sm9_dim_deinit(t0);
+		sm9_dim_deinit(t1);
+		sm9_dim_deinit(t2);
+		return -ENOMEM;
+	}
+
+	sm9_dim_mulm_u(t2, a[1], a[1], q);
+	sm9_dim_mulm(t0, a[0], a[0], q);
+	sm9_dim_subm(t2, t2, t0, q);
+	sm9_dim_invm(t2, t2, q, ctx->sys_cfg->q_minus_2);
+
+	sm9_dim_mulm(t0, a[0], t2, q);
+	sm9_dim_negm(out[0], t0, q);
+
+	sm9_dim_mulm(out[1], a[1], t2, q);
+
+	sm9_dim_deinit(t0);
+	sm9_dim_deinit(t1);
+	sm9_dim_deinit(t2);
+	return 0;
+}
+
+static int sm9_dim_fq4_is_zero(SM9_DIM_FQ4 a)
+{
+	return sm9_dim_is_zero(a[0]) && sm9_dim_is_zero(a[1]);
+}
+
+static int sm9_dim_fq4_negm(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a, struct sm9_ctx *ctx)
+{
+	sm9_dim_negm(out[0], a[0], ctx->sys_cfg->q);
+	sm9_dim_negm(out[1], a[1], ctx->sys_cfg->q);
+	return 0;
+}
+
+static int sm9_dim_fq4_conjugate(SM9_DIM_FQ4 out, SM9_DIM_FQ4 a,
+				 struct sm9_ctx *ctx)
+{
+	sm9_dim_set(out[0], a[0]);
+	sm9_dim_negm(out[1], a[1], ctx->sys_cfg->q);
+	return 0;
+}
+
+static size_t sm9_dim_fq4_get_size(SM9_DIM_FQ4 a)
+{
+	size_t size;
+
+	size = sm9_dim_get_size(a[0]);
+	size += sm9_dim_get_size(a[1]);
+	return size;
+}
+
+static ssize_t sm9_dim_fq4_to_buf_rev(SM9_DIM_FQ4 a, char *buf, size_t size)
+{
+	ssize_t d_size = size / 2, written_size;
+
+	written_size = sm9_dim_to_buf_rev(a[1], buf, d_size);
+	if (written_size < 0)
+		return written_size;
+
+	written_size = sm9_dim_to_buf_rev(a[0], buf + d_size, d_size);
+	if (written_size < 0)
+		return written_size;
+	return size;
+}
+
+int sm9_dim_fq12_set(SM9_DIM_FQ12 a, SM9_DIM_FQ12 b)
+{
+	sm9_dim_fq4_set(a[0], b[0]);
+	sm9_dim_fq4_set(a[1], b[1]);
+	sm9_dim_fq4_set(a[2], b[2]);
+	return 0;
+}
+
+void sm9_dim_fq12_deinit(SM9_DIM_FQ12 d)
+{
+	sm9_dim_fq4_deinit(d[0]);
+	sm9_dim_fq4_deinit(d[1]);
+	sm9_dim_fq4_deinit(d[2]);
+}
+
+int sm9_dim_fq12_init(SM9_DIM_FQ12 d, unsigned int nbits)
+{
+	int rc = 0, i, j;
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 2; j++)
+			sm9_dim_init(d[i][j], nbits);
+	}
+	return rc;
+}
+
+int sm9_dim_fq12_clear(SM9_DIM_FQ12 d)
+{
+	int i, j;
+
+	for (i = 0; i < 3; i++) {
+		for (j = 0; j < 2; j++)
+			sm9_dim_clear(d[i][j]);
+	}
+	return 0;
+}
+
+static int sm9_dim_fq12_mulm(SM9_DIM_FQ12 out, SM9_DIM_FQ12 _a, SM9_DIM_FQ12 _b,
+			     struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ4 tmp;
+	SM9_DIM_FQ12 a, b;
+	int rc;
+
+	rc = sm9_dim_fq4_init(tmp, 0);
+	rc |= sm9_dim_fq12_init(a, 0);
+	rc |= sm9_dim_fq12_init(b, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_fq12_set(a, _a);
+	sm9_dim_fq12_set(b, _b);
+
+	sm9_dim_fq4_mulm(out[0], a[0], b[0], ctx);
+	sm9_dim_fq4_mulm_v(tmp, a[1], b[2], ctx);
+	sm9_dim_fq4_addm(out[0], out[0], tmp, ctx);
+	sm9_dim_fq4_mulm_v(tmp, a[2], b[1], ctx);
+	sm9_dim_fq4_addm(out[0], out[0], tmp, ctx);
+
+	sm9_dim_fq4_mulm(out[1], a[0], b[1], ctx);
+	sm9_dim_fq4_mulm(tmp, a[1], b[0], ctx);
+	sm9_dim_fq4_addm(out[1], out[1], tmp, ctx);
+	sm9_dim_fq4_mulm_v(tmp, a[2], b[2], ctx);
+	sm9_dim_fq4_addm(out[1], out[1], tmp, ctx);
+
+	sm9_dim_fq4_mulm(out[2], a[0], b[2], ctx);
+	sm9_dim_fq4_mulm(tmp, a[1], b[1], ctx);
+	sm9_dim_fq4_addm(out[2], out[2], tmp, ctx);
+	sm9_dim_fq4_mulm(tmp, a[2], b[0], ctx);
+	sm9_dim_fq4_addm(out[2], out[2], tmp, ctx);
+
+out_free:
+	sm9_dim_fq12_deinit(a);
+	sm9_dim_fq12_deinit(b);
+	sm9_dim_fq4_deinit(tmp);
+	return rc;
+}
+
+int sm9_dim_fq12_powm(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a, MPI b,
+		      struct sm9_ctx *ctx)
+{
+	int i;
+	SM9_DIM_FQ12 tmp;
+	int rc = -EINVAL;
+
+	rc = sm9_dim_fq12_init(tmp, 0);
+	if (rc)
+		return -ENOMEM;
+	mpi_set_ui(tmp[0][0][0], 1);
+
+	for (i = mpi_get_nbits(b); i >= 0; i--) {
+		sm9_dim_fq12_mulm(tmp, tmp, tmp, ctx);
+		if (mpi_test_bit(b, i))
+			sm9_dim_fq12_mulm(tmp, a, tmp, ctx);
+	}
+	sm9_dim_fq12_set(out, tmp);
+
+	sm9_dim_fq12_deinit(tmp);
+	return rc;
+}
+EXPORT_SYMBOL_GPL(sm9_dim_fq12_powm);
+
+static int sm9_dim_fq12_powm_ui(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a,
+				unsigned int b, struct sm9_ctx *ctx)
+{
+	MPI i = NULL;
+	int rc;
+
+	i = mpi_new(0);
+	if (!i)
+		return -ENOMEM;
+
+	mpi_set_ui(i, b);
+	rc = sm9_dim_fq12_powm(out, a, i, ctx);
+	mpi_free(i);
+	return rc;
+}
+
+static int sm9_dim_fq12_invm(SM9_DIM_FQ12 out, SM9_DIM_FQ12 _a,
+			     struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ4 t0, t1, t2, t3;
+	SM9_DIM_FQ12 a;
+	int rc;
+
+	rc = sm9_dim_fq4_init(t0, 0);
+	rc |= sm9_dim_fq4_init(t1, 0);
+	rc |= sm9_dim_fq4_init(t2, 0);
+	rc |= sm9_dim_fq4_init(t3, 0);
+	rc |= sm9_dim_fq12_init(a, 0);
+	if (rc)
+		goto out_free;
+	sm9_dim_fq12_set(a, _a);
+
+	if (sm9_dim_fq4_is_zero(a[2])) {
+		sm9_dim_fq4_mulm(t0, a[0], a[0], ctx);
+		sm9_dim_fq4_mulm(t0, t0, a[0], ctx);
+		sm9_dim_fq4_mulm_v(t1, a[1], a[1], ctx);
+		sm9_dim_fq4_mulm(t1, t1, a[1], ctx);
+		sm9_dim_fq4_addm(t0, t0, t1, ctx);
+		sm9_dim_fq4_invm(t0, t0, ctx);
+
+		sm9_dim_fq4_mulm(out[2], a[1], a[1], ctx);
+		sm9_dim_fq4_mulm(out[2], out[2], t0, ctx);
+
+		sm9_dim_fq4_mulm(out[1], a[0], a[1], ctx);
+		sm9_dim_fq4_mulm(out[1], out[1], t0, ctx);
+		sm9_dim_fq4_negm(out[1], out[1], ctx);
+
+		sm9_dim_fq4_mulm(out[0], a[0], a[0], ctx);
+		sm9_dim_fq4_mulm(out[0], out[0], t0, ctx);
+		rc = 0;
+		goto out_free;
+	}
+
+	sm9_dim_fq4_mulm(t0, a[1], a[1], ctx);
+	sm9_dim_fq4_mulm(t1, a[0], a[2], ctx);
+	sm9_dim_fq4_subm(t0, t0, t1, ctx);
+
+	sm9_dim_fq4_mulm(t1, a[0], a[1], ctx);
+	sm9_dim_fq4_mulm_v(t2, a[2], a[2], ctx);
+	sm9_dim_fq4_subm(t1, t1, t2, ctx);
+
+	sm9_dim_fq4_mulm(t2, a[0], a[0], ctx);
+	sm9_dim_fq4_mulm_v(t3, a[1], a[2], ctx);
+	sm9_dim_fq4_subm(t2, t2, t3, ctx);
+
+	sm9_dim_fq4_mulm(t3, t1, t1, ctx);
+	sm9_dim_fq4_mulm(out[0], t0, t2, ctx);
+	sm9_dim_fq4_subm(t3, t3, out[0], ctx);
+	sm9_dim_fq4_invm(t3, t3, ctx);
+	sm9_dim_fq4_mulm(t3, a[2], t3, ctx);
+
+	sm9_dim_fq4_mulm(out[0], t2, t3, ctx);
+	sm9_dim_fq4_mulm(out[1], t1, t3, ctx);
+	sm9_dim_fq4_negm(out[1], out[1], ctx);
+	sm9_dim_fq4_mulm(out[2], t0, t3, ctx);
+
+	rc = 0;
+out_free:
+	sm9_dim_fq12_deinit(a);
+	sm9_dim_fq4_deinit(t0);
+	sm9_dim_fq4_deinit(t1);
+	sm9_dim_fq4_deinit(t2);
+	sm9_dim_fq4_deinit(t3);
+	return rc;
+}
+
+size_t sm9_dim_fq12_get_size(SM9_DIM_FQ12 a)
+{
+	size_t size;
+
+	size = sm9_dim_fq4_get_size(a[0]);
+	size += sm9_dim_fq4_get_size(a[1]);
+	size += sm9_dim_fq4_get_size(a[2]);
+	return size;
+}
+EXPORT_SYMBOL_GPL(sm9_dim_fq12_get_size);
+
+ssize_t sm9_dim_fq12_to_buf_rev(SM9_DIM_FQ12 a, char *buf, size_t size)
+{
+	ssize_t d_size = size / 3, written_size;
+
+	if (size % 3)
+		return -EINVAL;
+
+	written_size = sm9_dim_fq4_to_buf_rev(a[2], buf, d_size);
+	if (written_size < 0)
+		return written_size;
+
+	written_size = sm9_dim_fq4_to_buf_rev(a[1], buf + d_size, d_size);
+	if (written_size < 0)
+		return written_size;
+
+	written_size = sm9_dim_fq4_to_buf_rev(a[0], buf + 2 * d_size, d_size);
+	if (written_size < 0)
+		return written_size;
+	return size;
+}
+EXPORT_SYMBOL_GPL(sm9_dim_fq12_to_buf_rev);
+
+static int sm9_dim_fq12_frobenius_6(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a,
+				    struct sm9_ctx *ctx)
+{
+	sm9_dim_fq4_conjugate(out[0], a[0], ctx);
+	sm9_dim_fq4_conjugate(out[1], a[1], ctx);
+	sm9_dim_fq4_negm(out[1], out[1], ctx);
+	sm9_dim_fq4_conjugate(out[2], a[2], ctx);
+	return 0;
+}
+
+static int sm9_dim_fq12_frobenius_3(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a,
+				    struct sm9_ctx *ctx)
+{
+	MPI q = ctx->sys_cfg->q;
+	SM9_DIM_FQ2 beta;
+
+	if (sm9_dim_init(beta, 0))
+		return -ENOMEM;
+
+	mpi_set(beta[0], ctx->sys_cfg->beta);
+
+	sm9_dim_conjugate(out[0][0], a[0][0], q);
+	sm9_dim_conjugate(out[0][1], a[0][1], q);
+	sm9_dim_mulm(out[0][1], out[0][1], beta, q);
+	sm9_dim_negm(out[0][1], out[0][1], q);
+
+	sm9_dim_conjugate(out[1][0], a[1][0], q);
+	sm9_dim_mulm(out[1][0], out[1][0], beta, q);
+	sm9_dim_conjugate(out[1][1], a[1][1], q);
+
+	sm9_dim_conjugate(out[2][0], a[2][0], q);
+	sm9_dim_negm(out[2][0], out[2][0], q);
+	sm9_dim_conjugate(out[2][1], a[2][1], q);
+	sm9_dim_mulm(out[2][1], out[2][1], beta, q);
+
+	sm9_dim_deinit(beta);
+	return 0;
+}
+
+static int sm9_dim_fq12_frobenius_2(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a,
+				    struct sm9_ctx *ctx)
+{
+	sm9_dim_fq4_conjugate(out[0], a[0], ctx);
+	sm9_dim_fq4_conjugate(out[1], a[1], ctx);
+	sm9_dim_fq4_mulm_mpi(out[1], out[1], ctx->sys_cfg->alpha2, ctx);
+	sm9_dim_fq4_conjugate(out[2], a[2], ctx);
+	sm9_dim_fq4_mulm_mpi(out[2], out[2], ctx->sys_cfg->alpha4, ctx);
+	return 0;
+}
+
+static int sm9_dim_fq12_frobenius(SM9_DIM_FQ12 out, SM9_DIM_FQ12 a,
+				  struct sm9_ctx *ctx)
+{
+	MPI q = ctx->sys_cfg->q;
+
+	sm9_dim_conjugate(out[0][0], a[0][0], q);
+	sm9_dim_conjugate(out[0][1], a[0][1], q);
+	sm9_dim_mulm_mpi(out[0][1], out[0][1], ctx->sys_cfg->alpha3, q);
+
+	sm9_dim_conjugate(out[1][0], a[1][0], q);
+	sm9_dim_mulm_mpi(out[1][0], out[1][0], ctx->sys_cfg->alpha1, q);
+	sm9_dim_conjugate(out[1][1], a[1][1], q);
+	sm9_dim_mulm_mpi(out[1][1], out[1][1], ctx->sys_cfg->alpha4, q);
+
+	sm9_dim_conjugate(out[2][0], a[2][0], q);
+	sm9_dim_mulm_mpi(out[2][0], out[2][0], ctx->sys_cfg->alpha2, q);
+	sm9_dim_conjugate(out[2][1], a[2][1], q);
+	sm9_dim_mulm_mpi(out[2][1], out[2][1], ctx->sys_cfg->alpha5, q);
+	return 0;
+}
+
+static int g_V_V_Q(SM9_DIM_FQ12 num, SM9_DIM_FQ12 den, SM9_POINT V, MPI_POINT Q,
+		   struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 t0, t1, t2;
+	MPI q = ctx->sys_cfg->q;
+	int rc = -ENOMEM;
+
+	if (sm9_dim_init(t0, 0) || sm9_dim_init(t1, 0) || sm9_dim_init(t2, 0))
+		goto out_free;
+
+	sm9_dim_fq12_clear(num);
+	sm9_dim_fq12_clear(den);
+
+	sm9_dim_mulm(t0, V->z_fq2, V->z_fq2, q);
+	sm9_dim_mulm(t1, t0, V->z_fq2, q);
+	sm9_dim_mulm(den[0][1], t1, V->y_fq2, q);
+
+	sm9_dim_mulm_mpi(t2, den[0][1], Q->y, q);
+	sm9_dim_negm(num[0][1], t2, q);
+
+	sm9_dim_mulm(t1, V->x_fq2, V->x_fq2, q);
+	sm9_dim_mulm(t0, t0, t1, q);
+	sm9_dim_mulm_mpi(t0, t0, Q->x, q);
+	sm9_dim_mulm_ui(t0, t0, 3, q);
+	sm9_dim_div2m(num[2][0], t0, q);
+
+	sm9_dim_mulm(t1, t1, V->x_fq2, q);
+	sm9_dim_mulm_ui(t1, t1, 3, q);
+	sm9_dim_div2m(t1, t1, q);
+	sm9_dim_mulm(t0, V->y_fq2, V->y_fq2, q);
+	sm9_dim_subm(num[0][0], t0, t1, q);
+
+	rc = 0;
+
+out_free:
+	sm9_dim_deinit(t0);
+	sm9_dim_deinit(t1);
+	sm9_dim_deinit(t2);
+	return rc;
+}
+
+static int g_U_V_Q(SM9_DIM_FQ12 num, SM9_DIM_FQ12 den, SM9_POINT U, SM9_POINT V,
+		   MPI_POINT Q, struct sm9_ctx *ctx)
+{
+	SM9_DIM_FQ2 t0, t1, t2, t3, t4;
+	MPI q = ctx->sys_cfg->q;
+	int rc = -ENOMEM;
+
+	if (sm9_dim_init(t0, 0) || sm9_dim_init(t1, 0) || sm9_dim_init(t2, 0) ||
+	    sm9_dim_init(t3, 0) || sm9_dim_init(t4, 0))
+		goto out_free;
+
+	sm9_dim_fq12_clear(num);
+	sm9_dim_fq12_clear(den);
+
+	sm9_dim_mulm(t0, V->z_fq2, V->z_fq2, q);
+	sm9_dim_mulm(t1, t0, U->x_fq2, q);
+	sm9_dim_mulm(t0, t0, V->z_fq2, q);
+	sm9_dim_mulm(t2, U->z_fq2, U->z_fq2, q);
+	sm9_dim_mulm(t3, t2, V->x_fq2, q);
+	sm9_dim_mulm(t2, t2, U->z_fq2, q);
+	sm9_dim_mulm(t2, t2, V->y_fq2, q);
+	sm9_dim_subm(t1, t1, t3, q);
+	sm9_dim_mulm(t1, t1, U->z_fq2, q);
+	sm9_dim_mulm(t1, t1, V->z_fq2, q);
+	sm9_dim_mulm(den[0][1], t1, t0, q);
+	sm9_dim_mulm(t1, t1, V->y_fq2, q);
+	sm9_dim_mulm(t3, t0, U->y_fq2, q);
+	sm9_dim_subm(t3, t3, t2, q);
+	sm9_dim_mulm(t0, t0, t3, q);
+	sm9_dim_mulm_mpi(num[2][0], t0, Q->x, q);
+	sm9_dim_mulm(t3, t3, V->x_fq2, q);
+	sm9_dim_mulm(t3, t3, V->z_fq2, q);
+	sm9_dim_subm(num[0][0], t1, t3, q);
+	sm9_dim_mulm_mpi(t2, den[0][1], Q->y, q);
+	sm9_dim_negm(num[0][1], t2, q);
+
+	rc = 0;
+out_free:
+	sm9_dim_deinit(t0);
+	sm9_dim_deinit(t1);
+	sm9_dim_deinit(t2);
+	sm9_dim_deinit(t3);
+	sm9_dim_deinit(t4);
+
+	return rc;
+}
+
+static int sm9_point_pi_q(SM9_POINT out, SM9_POINT p, struct sm9_ctx *ctx)
+{
+	MPI q = ctx->sys_cfg->q;
+
+	if (!out)
+		return -EINVAL;
+
+	sm9_dim_conjugate(out->x_fq2, p->x_fq2, q);
+	sm9_dim_conjugate(out->y_fq2, p->y_fq2, q);
+	sm9_dim_conjugate(out->z_fq2, p->z_fq2, q);
+	sm9_dim_mulm_mpi(out->z_fq2, out->z_fq2, ctx->sys_cfg->pi_q_c, q);
+	return 0;
+}
+
+static int sm9_point_neg_pi_q2(SM9_POINT out, SM9_POINT p, struct sm9_ctx *ctx)
+{
+	if (!out)
+		return -EINVAL;
+
+	sm9_dim_copy(out->x_fq2, p->x_fq2);
+	sm9_dim_negm(out->y_fq2, p->y_fq2, ctx->sys_cfg->q);
+	sm9_dim_mulm_mpi(out->z_fq2, p->z_fq2, ctx->sys_cfg->pi_q2_c,
+			 ctx->sys_cfg->q);
+	return 0;
+}
+
+static int sm9_final_exponent(SM9_DIM_FQ12 out, SM9_DIM_FQ12 F,
+			      struct sm9_ctx *ctx)
+{
+	const char *a2_c = "0xd8000000019062ed0000b98b0cb27659";
+	const char *a3_c = "0x2400000000215d941";
+	SM9_DIM_FQ12 t0, t1, t2, t3, t4, t5;
+	MPI a2 = NULL, a3 = NULL;
+	int rc = -ENOMEM;
+
+	a2 = mpi_scanval(a2_c);
+	a3 = mpi_scanval(a3_c);
+	if (!a2 || !a3)
+		goto out_free_mpi;
+
+	rc = sm9_dim_fq12_init(t0, 0);
+	rc |= sm9_dim_fq12_init(t1, 0);
+	rc |= sm9_dim_fq12_init(t2, 0);
+	rc |= sm9_dim_fq12_init(t3, 0);
+	rc |= sm9_dim_fq12_init(t4, 0);
+	rc |= sm9_dim_fq12_init(t5, 0);
+	if (rc)
+		goto out_free;
+
+	sm9_dim_fq12_frobenius_6(t0, F, ctx);
+	sm9_dim_fq12_invm(t1, F, ctx);
+	sm9_dim_fq12_mulm(t0, t0, t1, ctx);
+	sm9_dim_fq12_frobenius_2(t1, t0, ctx);
+	sm9_dim_fq12_mulm(t0, t0, t1, ctx);
+
+	sm9_dim_fq12_powm(t2, t0, a3, ctx);
+	sm9_dim_fq12_invm(t2, t2, ctx);
+	sm9_dim_fq12_frobenius(t3, t2, ctx);
+	sm9_dim_fq12_mulm(t3, t2, t3, ctx);
+
+	sm9_dim_fq12_mulm(t2, t2, t3, ctx);
+	sm9_dim_fq12_frobenius(t4, t0, ctx);
+	sm9_dim_fq12_mulm(t5, t4, t0, ctx);
+	sm9_dim_fq12_powm_ui(t5, t5, 9, ctx);
+
+	sm9_dim_fq12_mulm(t2, t2, t5, ctx);
+	sm9_dim_fq12_mulm(t5, t0, t0, ctx);
+	sm9_dim_fq12_mulm(t5, t5, t5, ctx);
+	sm9_dim_fq12_mulm(t2, t2, t5, ctx);
+	sm9_dim_fq12_mulm(t4, t4, t4, ctx);
+	sm9_dim_fq12_mulm(t4, t4, t3, ctx);
+	sm9_dim_fq12_frobenius_2(t3, t0, ctx);
+	sm9_dim_fq12_mulm(t3, t3, t4, ctx);
+
+	sm9_dim_fq12_powm(t4, t3, a2, ctx);
+	sm9_dim_fq12_mulm(t2, t4, t2, ctx);
+	sm9_dim_fq12_frobenius_3(t3, t0, ctx);
+	sm9_dim_fq12_mulm(out, t3, t2, ctx);
+
+	rc = 0;
+
+out_free:
+	sm9_dim_fq12_deinit(t0);
+	sm9_dim_fq12_deinit(t1);
+	sm9_dim_fq12_deinit(t2);
+	sm9_dim_fq12_deinit(t3);
+	sm9_dim_fq12_deinit(t4);
+	sm9_dim_fq12_deinit(t5);
+
+out_free_mpi:
+	mpi_free(a2);
+	mpi_free(a3);
+	return rc;
+}
+
+int Rate_pairing(SM9_DIM_FQ12 out, SM9_POINT Q, MPI_POINT P, MPI a,
+		 struct sm9_ctx *ctx)
+{
+	SM9_POINT T = NULL;
+	SM9_POINT Q1 = NULL, Q2 = NULL;
+	SM9_DIM_FQ12 f_num, f_den, g_num, g_den;
+	int i;
+	int rc = -ENOMEM;
+
+	Q1 = sm9_point_new(0);
+	Q2 = sm9_point_new(0);
+	T = sm9_point_new(0);
+	if (!Q1 || !Q2 || !T || !P)
+		goto out_free;
+
+	if (sm9_dim_fq12_init(f_num, 0) || sm9_dim_fq12_init(f_den, 0) ||
+	    sm9_dim_fq12_init(g_num, 0) || sm9_dim_fq12_init(g_den, 0))
+		goto out_free;
+
+	/* f = 1 */
+	mpi_set_ui(f_num[0][0][0], 1);
+	mpi_set_ui(f_den[0][0][0], 1);
+
+	sm9_point_copy(T, Q);
+
+	for (i = mpi_get_nbits(a) - 2; i >= 0; i--) {
+		sm9_dim_fq12_mulm(f_num, f_num, f_num, ctx);
+		sm9_dim_fq12_mulm(f_den, f_den, f_den, ctx);
+		g_V_V_Q(g_num, g_den, T, P, ctx);
+		sm9_dim_fq12_mulm(f_num, f_num, g_num, ctx);
+		sm9_dim_fq12_mulm(f_den, f_den, g_den, ctx);
+
+		sm9_point_addm(T, T, T, ctx);
+
+		if (mpi_test_bit(a, i)) {
+			g_U_V_Q(g_num, g_den, T, Q, P, ctx);
+			sm9_dim_fq12_mulm(f_num, f_num, g_num, ctx);
+			sm9_dim_fq12_mulm(f_den, f_den, g_den, ctx);
+			sm9_point_addm(T, T, Q, ctx);
+		}
+	}
+
+	sm9_point_pi_q(Q1, Q, ctx);
+	sm9_point_neg_pi_q2(Q2, Q, ctx);
+
+	g_U_V_Q(g_num, g_den, T, Q1, P, ctx);
+	sm9_dim_fq12_mulm(f_num, f_num, g_num, ctx);
+	sm9_dim_fq12_mulm(f_den, f_den, g_den, ctx);
+	sm9_point_addm(T, T, Q1, ctx);
+
+	g_U_V_Q(g_num, g_den, T, Q2, P, ctx);
+	sm9_dim_fq12_mulm(f_num, f_num, g_num, ctx);
+	sm9_dim_fq12_mulm(f_den, f_den, g_den, ctx);
+	sm9_point_addm(T, T, Q2, ctx);
+
+	sm9_dim_fq12_invm(f_den, f_den, ctx);
+	sm9_dim_fq12_mulm(out, f_num, f_den, ctx);
+
+	sm9_final_exponent(out, out, ctx);
+
+	rc = 0;
+out_free:
+	sm9_dim_fq12_deinit(f_num);
+	sm9_dim_fq12_deinit(f_den);
+	sm9_dim_fq12_deinit(g_num);
+	sm9_dim_fq12_deinit(g_den);
+	sm9_point_release(T);
+	sm9_point_release(Q1);
+	sm9_point_release(Q2);
+	return rc;
+}
+
+MPI_POINT bytes_to_point(u8 *src, size_t size)
+{
+	MPI_POINT res;
+	MPI tmp;
+	size_t l = size / 2;
+
+	if (size % 2)
+		return NULL;
+
+	res = mpi_point_new(0);
+	if (!res)
+		return NULL;
+	mpi_set_ui(res->z, 1);
+
+	tmp = mpi_read_raw_data(src, l);
+	if (!tmp) {
+		mpi_point_release(res);
+		return NULL;
+	}
+	mpi_set(res->x, tmp);
+	mpi_free(tmp);
+
+	tmp = mpi_read_raw_data(src + l, l);
+	if (!tmp) {
+		mpi_point_release(res);
+		return NULL;
+	}
+	mpi_set(res->y, tmp);
+	mpi_free(tmp);
+	return res;
+}
+
+int point_to_bytes(MPI_POINT P, u8 **out, size_t *out_size)
+{
+	size_t size;
+	unsigned int write_size;
+
+	if (!P)
+		return -EINVAL;
+
+	size = mpi_get_size(P->x);
+	*out = kzalloc(size * 2, GFP_KERNEL);
+	if (!*out)
+		return -ENOMEM;
+
+	mpi_read_buffer(P->x, *out, size, &write_size, NULL);
+	if (write_size != size) {
+		kfree(*out);
+		*out = NULL;
+		return -EINVAL;
+	}
+
+	mpi_read_buffer(P->y, *out + size, size, &write_size, NULL);
+	if (write_size != size) {
+		kfree(*out);
+		*out = NULL;
+		return -EINVAL;
+	}
+
+	*out_size = size * 2;
+	return 0;
+}
+
+int mpi_point_to_sm9_point(SM9_POINT out, MPI_POINT p)
+{
+	if (!out)
+		return -EINVAL;
+
+	mpi_set(out->xd1, p->x);
+	mpi_set_ui(out->xd2, 0);
+	mpi_set(out->yd1, p->y);
+	mpi_set_ui(out->yd2, 0);
+	mpi_set(out->zd1, p->z);
+	mpi_set_ui(out->zd2, 0);
+	return 0;
+}
+EXPORT_SYMBOL_GPL(mpi_point_to_sm9_point);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("GUO Zihua <guozihua@huawei.com>");
+MODULE_DESCRIPTION("SM9 generic algorithm library");
diff --git a/crypto/sm9_lib.h b/crypto/sm9_lib.h
new file mode 100644
index 000000000000..27c741ca11cd
--- /dev/null
+++ b/crypto/sm9_lib.h
@@ -0,0 +1,92 @@ 
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ *
+ * Header of libraries for SM9 key exchange algorithm
+ *
+ * Copyright (c) 2023, Huawei Technology Co., Ltd.
+ * Authors: GUO Zihua <guozihua@huawei.com>
+ */
+
+#include <linux/mpi.h>
+#include <crypto/sm9.h>
+
+#ifndef _LOCAL_SM9_LIB_H
+#define _LOCAL_SM9_LIB_H
+
+struct sm9_point_fq2 {
+	union {
+		SM9_DIM_FQ2 x_fq2;
+		struct {
+			MPI xd1;
+			MPI xd2;
+		};
+	};
+	union {
+		SM9_DIM_FQ2 y_fq2;
+		struct {
+			MPI yd1;
+			MPI yd2;
+		};
+	};
+	union {
+		SM9_DIM_FQ2 z_fq2;
+		struct {
+			MPI zd1;
+			MPI zd2;
+		};
+	};
+};
+
+struct sm9_sys_cfg {
+	u8 cid;
+	MPI q;
+	MPI q_minus_2;
+	MPI q2;
+	MPI q2_minus_2;
+	MPI a, b;
+	MPI N;
+	MPI N_minus_1;
+	unsigned int N_log_2_times_5_roundup;
+	size_t N_size;
+	unsigned int cf;
+	unsigned int k;
+	MPI_POINT P1;
+	SM9_POINT P2;
+	u8 hid;
+	MPI t;
+	MPI tr;
+	MPI pairing_a;
+	MPI pi_q_c;
+	MPI pi_q2_c;
+	MPI beta;
+	MPI alpha1;
+	MPI alpha2;
+	MPI alpha3;
+	MPI alpha4;
+	MPI alpha5;
+
+	struct mpi_ec_ctx *G1;
+};
+
+struct sm9_ctx {
+	struct sm9_sys_cfg *sys_cfg;
+	MPI_POINT R;
+	MPI_POINT Ppub_s;
+	MPI r;
+	SM9_POINT de;
+	unsigned int hid;
+	char *id;
+	size_t id_size;
+	bool initiator;
+};
+
+int Rate_pairing(SM9_DIM_FQ12 out, SM9_POINT Q, MPI_POINT P, MPI t,
+		 struct sm9_ctx *ctx);
+MPI_POINT bytes_to_point(u8 *src, size_t size);
+int point_to_bytes(MPI_POINT P, u8 **out, size_t *out_size);
+
+int sm9_point_mpi_addm(SM9_POINT out, SM9_POINT P, MPI a, MPI q);
+int sm9_point_mpi_mulm(SM9_POINT out, SM9_POINT P, MPI a, struct sm9_ctx *ctx);
+int sm9_point_addm(SM9_POINT out, SM9_POINT P, SM9_POINT Q,
+		   struct sm9_ctx *ctx);
+int mpi_point_to_sm9_point(SM9_POINT out, MPI_POINT p);
+#endif