18c2ecf20Sopenharmony_ci/* ec.c -  Elliptic Curve functions
28c2ecf20Sopenharmony_ci * Copyright (C) 2007 Free Software Foundation, Inc.
38c2ecf20Sopenharmony_ci * Copyright (C) 2013 g10 Code GmbH
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file is part of Libgcrypt.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Libgcrypt is free software; you can redistribute it and/or modify
88c2ecf20Sopenharmony_ci * it under the terms of the GNU Lesser General Public License as
98c2ecf20Sopenharmony_ci * published by the Free Software Foundation; either version 2.1 of
108c2ecf20Sopenharmony_ci * the License, or (at your option) any later version.
118c2ecf20Sopenharmony_ci *
128c2ecf20Sopenharmony_ci * Libgcrypt is distributed in the hope that it will be useful,
138c2ecf20Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of
148c2ecf20Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
158c2ecf20Sopenharmony_ci * GNU Lesser General Public License for more details.
168c2ecf20Sopenharmony_ci *
178c2ecf20Sopenharmony_ci * You should have received a copy of the GNU Lesser General Public
188c2ecf20Sopenharmony_ci * License along with this program; if not, see <http://www.gnu.org/licenses/>.
198c2ecf20Sopenharmony_ci */
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci#include "mpi-internal.h"
228c2ecf20Sopenharmony_ci#include "longlong.h"
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci#define point_init(a)  mpi_point_init((a))
258c2ecf20Sopenharmony_ci#define point_free(a)  mpi_point_free_parts((a))
268c2ecf20Sopenharmony_ci
278c2ecf20Sopenharmony_ci#define log_error(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
288c2ecf20Sopenharmony_ci#define log_fatal(fmt, ...) pr_err(fmt, ##__VA_ARGS__)
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci#define DIM(v) (sizeof(v)/sizeof((v)[0]))
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_ci
338c2ecf20Sopenharmony_ci/* Create a new point option.  NBITS gives the size in bits of one
348c2ecf20Sopenharmony_ci * coordinate; it is only used to pre-allocate some resources and
358c2ecf20Sopenharmony_ci * might also be passed as 0 to use a default value.
368c2ecf20Sopenharmony_ci */
378c2ecf20Sopenharmony_ciMPI_POINT mpi_point_new(unsigned int nbits)
388c2ecf20Sopenharmony_ci{
398c2ecf20Sopenharmony_ci	MPI_POINT p;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	(void)nbits;  /* Currently not used.  */
428c2ecf20Sopenharmony_ci
438c2ecf20Sopenharmony_ci	p = kmalloc(sizeof(*p), GFP_KERNEL);
448c2ecf20Sopenharmony_ci	if (p)
458c2ecf20Sopenharmony_ci		mpi_point_init(p);
468c2ecf20Sopenharmony_ci	return p;
478c2ecf20Sopenharmony_ci}
488c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_point_new);
498c2ecf20Sopenharmony_ci
508c2ecf20Sopenharmony_ci/* Release the point object P.  P may be NULL. */
518c2ecf20Sopenharmony_civoid mpi_point_release(MPI_POINT p)
528c2ecf20Sopenharmony_ci{
538c2ecf20Sopenharmony_ci	if (p) {
548c2ecf20Sopenharmony_ci		mpi_point_free_parts(p);
558c2ecf20Sopenharmony_ci		kfree(p);
568c2ecf20Sopenharmony_ci	}
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_point_release);
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci/* Initialize the fields of a point object.  gcry_mpi_point_free_parts
618c2ecf20Sopenharmony_ci * may be used to release the fields.
628c2ecf20Sopenharmony_ci */
638c2ecf20Sopenharmony_civoid mpi_point_init(MPI_POINT p)
648c2ecf20Sopenharmony_ci{
658c2ecf20Sopenharmony_ci	p->x = mpi_new(0);
668c2ecf20Sopenharmony_ci	p->y = mpi_new(0);
678c2ecf20Sopenharmony_ci	p->z = mpi_new(0);
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_point_init);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci/* Release the parts of a point object. */
728c2ecf20Sopenharmony_civoid mpi_point_free_parts(MPI_POINT p)
738c2ecf20Sopenharmony_ci{
748c2ecf20Sopenharmony_ci	mpi_free(p->x); p->x = NULL;
758c2ecf20Sopenharmony_ci	mpi_free(p->y); p->y = NULL;
768c2ecf20Sopenharmony_ci	mpi_free(p->z); p->z = NULL;
778c2ecf20Sopenharmony_ci}
788c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_point_free_parts);
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci/* Set the value from S into D.  */
818c2ecf20Sopenharmony_cistatic void point_set(MPI_POINT d, MPI_POINT s)
828c2ecf20Sopenharmony_ci{
838c2ecf20Sopenharmony_ci	mpi_set(d->x, s->x);
848c2ecf20Sopenharmony_ci	mpi_set(d->y, s->y);
858c2ecf20Sopenharmony_ci	mpi_set(d->z, s->z);
868c2ecf20Sopenharmony_ci}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_cistatic void point_resize(MPI_POINT p, struct mpi_ec_ctx *ctx)
898c2ecf20Sopenharmony_ci{
908c2ecf20Sopenharmony_ci	size_t nlimbs = ctx->p->nlimbs;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	mpi_resize(p->x, nlimbs);
938c2ecf20Sopenharmony_ci	p->x->nlimbs = nlimbs;
948c2ecf20Sopenharmony_ci	mpi_resize(p->z, nlimbs);
958c2ecf20Sopenharmony_ci	p->z->nlimbs = nlimbs;
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_ci	if (ctx->model != MPI_EC_MONTGOMERY) {
988c2ecf20Sopenharmony_ci		mpi_resize(p->y, nlimbs);
998c2ecf20Sopenharmony_ci		p->y->nlimbs = nlimbs;
1008c2ecf20Sopenharmony_ci	}
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void point_swap_cond(MPI_POINT d, MPI_POINT s, unsigned long swap,
1048c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	mpi_swap_cond(d->x, s->x, swap);
1078c2ecf20Sopenharmony_ci	if (ctx->model != MPI_EC_MONTGOMERY)
1088c2ecf20Sopenharmony_ci		mpi_swap_cond(d->y, s->y, swap);
1098c2ecf20Sopenharmony_ci	mpi_swap_cond(d->z, s->z, swap);
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci/* W = W mod P.  */
1148c2ecf20Sopenharmony_cistatic void ec_mod(MPI w, struct mpi_ec_ctx *ec)
1158c2ecf20Sopenharmony_ci{
1168c2ecf20Sopenharmony_ci	if (ec->t.p_barrett)
1178c2ecf20Sopenharmony_ci		mpi_mod_barrett(w, w, ec->t.p_barrett);
1188c2ecf20Sopenharmony_ci	else
1198c2ecf20Sopenharmony_ci		mpi_mod(w, w, ec->p);
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic void ec_addm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	mpi_add(w, u, v);
1258c2ecf20Sopenharmony_ci	ec_mod(w, ctx);
1268c2ecf20Sopenharmony_ci}
1278c2ecf20Sopenharmony_ci
1288c2ecf20Sopenharmony_cistatic void ec_subm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ec)
1298c2ecf20Sopenharmony_ci{
1308c2ecf20Sopenharmony_ci	mpi_sub(w, u, v);
1318c2ecf20Sopenharmony_ci	while (w->sign)
1328c2ecf20Sopenharmony_ci		mpi_add(w, w, ec->p);
1338c2ecf20Sopenharmony_ci	/*ec_mod(w, ec);*/
1348c2ecf20Sopenharmony_ci}
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_cistatic void ec_mulm(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
1378c2ecf20Sopenharmony_ci{
1388c2ecf20Sopenharmony_ci	mpi_mul(w, u, v);
1398c2ecf20Sopenharmony_ci	ec_mod(w, ctx);
1408c2ecf20Sopenharmony_ci}
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci/* W = 2 * U mod P.  */
1438c2ecf20Sopenharmony_cistatic void ec_mul2(MPI w, MPI u, struct mpi_ec_ctx *ctx)
1448c2ecf20Sopenharmony_ci{
1458c2ecf20Sopenharmony_ci	mpi_lshift(w, u, 1);
1468c2ecf20Sopenharmony_ci	ec_mod(w, ctx);
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_cistatic void ec_powm(MPI w, const MPI b, const MPI e,
1508c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
1518c2ecf20Sopenharmony_ci{
1528c2ecf20Sopenharmony_ci	mpi_powm(w, b, e, ctx->p);
1538c2ecf20Sopenharmony_ci	/* mpi_abs(w); */
1548c2ecf20Sopenharmony_ci}
1558c2ecf20Sopenharmony_ci
1568c2ecf20Sopenharmony_ci/* Shortcut for
1578c2ecf20Sopenharmony_ci * ec_powm(B, B, mpi_const(MPI_C_TWO), ctx);
1588c2ecf20Sopenharmony_ci * for easier optimization.
1598c2ecf20Sopenharmony_ci */
1608c2ecf20Sopenharmony_cistatic void ec_pow2(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
1618c2ecf20Sopenharmony_ci{
1628c2ecf20Sopenharmony_ci	/* Using mpi_mul is slightly faster (at least on amd64).  */
1638c2ecf20Sopenharmony_ci	/* mpi_powm(w, b, mpi_const(MPI_C_TWO), ctx->p); */
1648c2ecf20Sopenharmony_ci	ec_mulm(w, b, b, ctx);
1658c2ecf20Sopenharmony_ci}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci/* Shortcut for
1688c2ecf20Sopenharmony_ci * ec_powm(B, B, mpi_const(MPI_C_THREE), ctx);
1698c2ecf20Sopenharmony_ci * for easier optimization.
1708c2ecf20Sopenharmony_ci */
1718c2ecf20Sopenharmony_cistatic void ec_pow3(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
1728c2ecf20Sopenharmony_ci{
1738c2ecf20Sopenharmony_ci	mpi_powm(w, b, mpi_const(MPI_C_THREE), ctx->p);
1748c2ecf20Sopenharmony_ci}
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic void ec_invm(MPI x, MPI a, struct mpi_ec_ctx *ctx)
1778c2ecf20Sopenharmony_ci{
1788c2ecf20Sopenharmony_ci	if (!mpi_invm(x, a, ctx->p))
1798c2ecf20Sopenharmony_ci		log_error("ec_invm: inverse does not exist:\n");
1808c2ecf20Sopenharmony_ci}
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_cistatic void mpih_set_cond(mpi_ptr_t wp, mpi_ptr_t up,
1838c2ecf20Sopenharmony_ci		mpi_size_t usize, unsigned long set)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	mpi_size_t i;
1868c2ecf20Sopenharmony_ci	mpi_limb_t mask = ((mpi_limb_t)0) - set;
1878c2ecf20Sopenharmony_ci	mpi_limb_t x;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	for (i = 0; i < usize; i++) {
1908c2ecf20Sopenharmony_ci		x = mask & (wp[i] ^ up[i]);
1918c2ecf20Sopenharmony_ci		wp[i] = wp[i] ^ x;
1928c2ecf20Sopenharmony_ci	}
1938c2ecf20Sopenharmony_ci}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci/* Routines for 2^255 - 19.  */
1968c2ecf20Sopenharmony_ci
1978c2ecf20Sopenharmony_ci#define LIMB_SIZE_25519 ((256+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
1988c2ecf20Sopenharmony_ci
1998c2ecf20Sopenharmony_cistatic void ec_addm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
2008c2ecf20Sopenharmony_ci{
2018c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
2028c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_25519;
2038c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_25519];
2048c2ecf20Sopenharmony_ci	mpi_limb_t borrow;
2058c2ecf20Sopenharmony_ci
2068c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
2078c2ecf20Sopenharmony_ci		log_bug("addm_25519: different sizes\n");
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	memset(n, 0, sizeof(n));
2108c2ecf20Sopenharmony_ci	up = u->d;
2118c2ecf20Sopenharmony_ci	vp = v->d;
2128c2ecf20Sopenharmony_ci	wp = w->d;
2138c2ecf20Sopenharmony_ci
2148c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, up, vp, wsize);
2158c2ecf20Sopenharmony_ci	borrow = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
2168c2ecf20Sopenharmony_ci	mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
2178c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, n, wsize);
2188c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
2198c2ecf20Sopenharmony_ci}
2208c2ecf20Sopenharmony_ci
2218c2ecf20Sopenharmony_cistatic void ec_subm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
2228c2ecf20Sopenharmony_ci{
2238c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
2248c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_25519;
2258c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_25519];
2268c2ecf20Sopenharmony_ci	mpi_limb_t borrow;
2278c2ecf20Sopenharmony_ci
2288c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
2298c2ecf20Sopenharmony_ci		log_bug("subm_25519: different sizes\n");
2308c2ecf20Sopenharmony_ci
2318c2ecf20Sopenharmony_ci	memset(n, 0, sizeof(n));
2328c2ecf20Sopenharmony_ci	up = u->d;
2338c2ecf20Sopenharmony_ci	vp = v->d;
2348c2ecf20Sopenharmony_ci	wp = w->d;
2358c2ecf20Sopenharmony_ci
2368c2ecf20Sopenharmony_ci	borrow = mpihelp_sub_n(wp, up, vp, wsize);
2378c2ecf20Sopenharmony_ci	mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
2388c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, n, wsize);
2398c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
2408c2ecf20Sopenharmony_ci}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_cistatic void ec_mulm_25519(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
2438c2ecf20Sopenharmony_ci{
2448c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
2458c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_25519;
2468c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_25519*2];
2478c2ecf20Sopenharmony_ci	mpi_limb_t m[LIMB_SIZE_25519+1];
2488c2ecf20Sopenharmony_ci	mpi_limb_t cy;
2498c2ecf20Sopenharmony_ci	int msb;
2508c2ecf20Sopenharmony_ci
2518c2ecf20Sopenharmony_ci	(void)ctx;
2528c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
2538c2ecf20Sopenharmony_ci		log_bug("mulm_25519: different sizes\n");
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci	up = u->d;
2568c2ecf20Sopenharmony_ci	vp = v->d;
2578c2ecf20Sopenharmony_ci	wp = w->d;
2588c2ecf20Sopenharmony_ci
2598c2ecf20Sopenharmony_ci	mpihelp_mul_n(n, up, vp, wsize);
2608c2ecf20Sopenharmony_ci	memcpy(wp, n, wsize * BYTES_PER_MPI_LIMB);
2618c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
2628c2ecf20Sopenharmony_ci
2638c2ecf20Sopenharmony_ci	memcpy(m, n+LIMB_SIZE_25519-1, (wsize+1) * BYTES_PER_MPI_LIMB);
2648c2ecf20Sopenharmony_ci	mpihelp_rshift(m, m, LIMB_SIZE_25519+1, (255 % BITS_PER_MPI_LIMB));
2658c2ecf20Sopenharmony_ci
2668c2ecf20Sopenharmony_ci	memcpy(n, m, wsize * BYTES_PER_MPI_LIMB);
2678c2ecf20Sopenharmony_ci	cy = mpihelp_lshift(m, m, LIMB_SIZE_25519, 4);
2688c2ecf20Sopenharmony_ci	m[LIMB_SIZE_25519] = cy;
2698c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(m, m, n, wsize);
2708c2ecf20Sopenharmony_ci	m[LIMB_SIZE_25519] += cy;
2718c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(m, m, n, wsize);
2728c2ecf20Sopenharmony_ci	m[LIMB_SIZE_25519] += cy;
2738c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(m, m, n, wsize);
2748c2ecf20Sopenharmony_ci	m[LIMB_SIZE_25519] += cy;
2758c2ecf20Sopenharmony_ci
2768c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(wp, wp, m, wsize);
2778c2ecf20Sopenharmony_ci	m[LIMB_SIZE_25519] += cy;
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci	memset(m, 0, wsize * BYTES_PER_MPI_LIMB);
2808c2ecf20Sopenharmony_ci	msb = (wp[LIMB_SIZE_25519-1] >> (255 % BITS_PER_MPI_LIMB));
2818c2ecf20Sopenharmony_ci	m[0] = (m[LIMB_SIZE_25519] * 2 + msb) * 19;
2828c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_25519-1] &= ~((mpi_limb_t)1 << (255 % BITS_PER_MPI_LIMB));
2838c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, m, wsize);
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	m[0] = 0;
2868c2ecf20Sopenharmony_ci	cy = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
2878c2ecf20Sopenharmony_ci	mpih_set_cond(m, ctx->p->d, wsize, (cy != 0UL));
2888c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, m, wsize);
2898c2ecf20Sopenharmony_ci}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_cistatic void ec_mul2_25519(MPI w, MPI u, struct mpi_ec_ctx *ctx)
2928c2ecf20Sopenharmony_ci{
2938c2ecf20Sopenharmony_ci	ec_addm_25519(w, u, u, ctx);
2948c2ecf20Sopenharmony_ci}
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cistatic void ec_pow2_25519(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
2978c2ecf20Sopenharmony_ci{
2988c2ecf20Sopenharmony_ci	ec_mulm_25519(w, b, b, ctx);
2998c2ecf20Sopenharmony_ci}
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci/* Routines for 2^448 - 2^224 - 1.  */
3028c2ecf20Sopenharmony_ci
3038c2ecf20Sopenharmony_ci#define LIMB_SIZE_448 ((448+BITS_PER_MPI_LIMB-1)/BITS_PER_MPI_LIMB)
3048c2ecf20Sopenharmony_ci#define LIMB_SIZE_HALF_448 ((LIMB_SIZE_448+1)/2)
3058c2ecf20Sopenharmony_ci
3068c2ecf20Sopenharmony_cistatic void ec_addm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
3078c2ecf20Sopenharmony_ci{
3088c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
3098c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_448;
3108c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_448];
3118c2ecf20Sopenharmony_ci	mpi_limb_t cy;
3128c2ecf20Sopenharmony_ci
3138c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
3148c2ecf20Sopenharmony_ci		log_bug("addm_448: different sizes\n");
3158c2ecf20Sopenharmony_ci
3168c2ecf20Sopenharmony_ci	memset(n, 0, sizeof(n));
3178c2ecf20Sopenharmony_ci	up = u->d;
3188c2ecf20Sopenharmony_ci	vp = v->d;
3198c2ecf20Sopenharmony_ci	wp = w->d;
3208c2ecf20Sopenharmony_ci
3218c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(wp, up, vp, wsize);
3228c2ecf20Sopenharmony_ci	mpih_set_cond(n, ctx->p->d, wsize, (cy != 0UL));
3238c2ecf20Sopenharmony_ci	mpihelp_sub_n(wp, wp, n, wsize);
3248c2ecf20Sopenharmony_ci}
3258c2ecf20Sopenharmony_ci
3268c2ecf20Sopenharmony_cistatic void ec_subm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
3278c2ecf20Sopenharmony_ci{
3288c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
3298c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_448;
3308c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_448];
3318c2ecf20Sopenharmony_ci	mpi_limb_t borrow;
3328c2ecf20Sopenharmony_ci
3338c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
3348c2ecf20Sopenharmony_ci		log_bug("subm_448: different sizes\n");
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	memset(n, 0, sizeof(n));
3378c2ecf20Sopenharmony_ci	up = u->d;
3388c2ecf20Sopenharmony_ci	vp = v->d;
3398c2ecf20Sopenharmony_ci	wp = w->d;
3408c2ecf20Sopenharmony_ci
3418c2ecf20Sopenharmony_ci	borrow = mpihelp_sub_n(wp, up, vp, wsize);
3428c2ecf20Sopenharmony_ci	mpih_set_cond(n, ctx->p->d, wsize, (borrow != 0UL));
3438c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, n, wsize);
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic void ec_mulm_448(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx)
3478c2ecf20Sopenharmony_ci{
3488c2ecf20Sopenharmony_ci	mpi_ptr_t wp, up, vp;
3498c2ecf20Sopenharmony_ci	mpi_size_t wsize = LIMB_SIZE_448;
3508c2ecf20Sopenharmony_ci	mpi_limb_t n[LIMB_SIZE_448*2];
3518c2ecf20Sopenharmony_ci	mpi_limb_t a2[LIMB_SIZE_HALF_448];
3528c2ecf20Sopenharmony_ci	mpi_limb_t a3[LIMB_SIZE_HALF_448];
3538c2ecf20Sopenharmony_ci	mpi_limb_t b0[LIMB_SIZE_HALF_448];
3548c2ecf20Sopenharmony_ci	mpi_limb_t b1[LIMB_SIZE_HALF_448];
3558c2ecf20Sopenharmony_ci	mpi_limb_t cy;
3568c2ecf20Sopenharmony_ci	int i;
3578c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
3588c2ecf20Sopenharmony_ci	mpi_limb_t b1_rest, a3_rest;
3598c2ecf20Sopenharmony_ci#endif
3608c2ecf20Sopenharmony_ci
3618c2ecf20Sopenharmony_ci	if (w->nlimbs != wsize || u->nlimbs != wsize || v->nlimbs != wsize)
3628c2ecf20Sopenharmony_ci		log_bug("mulm_448: different sizes\n");
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	up = u->d;
3658c2ecf20Sopenharmony_ci	vp = v->d;
3668c2ecf20Sopenharmony_ci	wp = w->d;
3678c2ecf20Sopenharmony_ci
3688c2ecf20Sopenharmony_ci	mpihelp_mul_n(n, up, vp, wsize);
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	for (i = 0; i < (wsize + 1) / 2; i++) {
3718c2ecf20Sopenharmony_ci		b0[i] = n[i];
3728c2ecf20Sopenharmony_ci		b1[i] = n[i+wsize/2];
3738c2ecf20Sopenharmony_ci		a2[i] = n[i+wsize];
3748c2ecf20Sopenharmony_ci		a3[i] = n[i+wsize+wsize/2];
3758c2ecf20Sopenharmony_ci	}
3768c2ecf20Sopenharmony_ci
3778c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
3788c2ecf20Sopenharmony_ci	b0[LIMB_SIZE_HALF_448-1] &= ((mpi_limb_t)1UL << 32)-1;
3798c2ecf20Sopenharmony_ci	a2[LIMB_SIZE_HALF_448-1] &= ((mpi_limb_t)1UL << 32)-1;
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	b1_rest = 0;
3828c2ecf20Sopenharmony_ci	a3_rest = 0;
3838c2ecf20Sopenharmony_ci
3848c2ecf20Sopenharmony_ci	for (i = (wsize + 1) / 2 - 1; i >= 0; i--) {
3858c2ecf20Sopenharmony_ci		mpi_limb_t b1v, a3v;
3868c2ecf20Sopenharmony_ci		b1v = b1[i];
3878c2ecf20Sopenharmony_ci		a3v = a3[i];
3888c2ecf20Sopenharmony_ci		b1[i] = (b1_rest << 32) | (b1v >> 32);
3898c2ecf20Sopenharmony_ci		a3[i] = (a3_rest << 32) | (a3v >> 32);
3908c2ecf20Sopenharmony_ci		b1_rest = b1v & (((mpi_limb_t)1UL << 32)-1);
3918c2ecf20Sopenharmony_ci		a3_rest = a3v & (((mpi_limb_t)1UL << 32)-1);
3928c2ecf20Sopenharmony_ci	}
3938c2ecf20Sopenharmony_ci#endif
3948c2ecf20Sopenharmony_ci
3958c2ecf20Sopenharmony_ci	cy = mpihelp_add_n(b0, b0, a2, LIMB_SIZE_HALF_448);
3968c2ecf20Sopenharmony_ci	cy += mpihelp_add_n(b0, b0, a3, LIMB_SIZE_HALF_448);
3978c2ecf20Sopenharmony_ci	for (i = 0; i < (wsize + 1) / 2; i++)
3988c2ecf20Sopenharmony_ci		wp[i] = b0[i];
3998c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
4008c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_HALF_448-1] &= (((mpi_limb_t)1UL << 32)-1);
4018c2ecf20Sopenharmony_ci#endif
4028c2ecf20Sopenharmony_ci
4038c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
4048c2ecf20Sopenharmony_ci	cy = b0[LIMB_SIZE_HALF_448-1] >> 32;
4058c2ecf20Sopenharmony_ci#endif
4068c2ecf20Sopenharmony_ci
4078c2ecf20Sopenharmony_ci	cy = mpihelp_add_1(b1, b1, LIMB_SIZE_HALF_448, cy);
4088c2ecf20Sopenharmony_ci	cy += mpihelp_add_n(b1, b1, a2, LIMB_SIZE_HALF_448);
4098c2ecf20Sopenharmony_ci	cy += mpihelp_add_n(b1, b1, a3, LIMB_SIZE_HALF_448);
4108c2ecf20Sopenharmony_ci	cy += mpihelp_add_n(b1, b1, a3, LIMB_SIZE_HALF_448);
4118c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
4128c2ecf20Sopenharmony_ci	b1_rest = 0;
4138c2ecf20Sopenharmony_ci	for (i = (wsize + 1) / 2 - 1; i >= 0; i--) {
4148c2ecf20Sopenharmony_ci		mpi_limb_t b1v = b1[i];
4158c2ecf20Sopenharmony_ci		b1[i] = (b1_rest << 32) | (b1v >> 32);
4168c2ecf20Sopenharmony_ci		b1_rest = b1v & (((mpi_limb_t)1UL << 32)-1);
4178c2ecf20Sopenharmony_ci	}
4188c2ecf20Sopenharmony_ci	wp[LIMB_SIZE_HALF_448-1] |= (b1_rest << 32);
4198c2ecf20Sopenharmony_ci#endif
4208c2ecf20Sopenharmony_ci	for (i = 0; i < wsize / 2; i++)
4218c2ecf20Sopenharmony_ci		wp[i+(wsize + 1) / 2] = b1[i];
4228c2ecf20Sopenharmony_ci
4238c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
4248c2ecf20Sopenharmony_ci	cy = b1[LIMB_SIZE_HALF_448-1];
4258c2ecf20Sopenharmony_ci#endif
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci	memset(n, 0, wsize * BYTES_PER_MPI_LIMB);
4288c2ecf20Sopenharmony_ci
4298c2ecf20Sopenharmony_ci#if (LIMB_SIZE_HALF_448 > LIMB_SIZE_448/2)
4308c2ecf20Sopenharmony_ci	n[LIMB_SIZE_HALF_448-1] = cy << 32;
4318c2ecf20Sopenharmony_ci#else
4328c2ecf20Sopenharmony_ci	n[LIMB_SIZE_HALF_448] = cy;
4338c2ecf20Sopenharmony_ci#endif
4348c2ecf20Sopenharmony_ci	n[0] = cy;
4358c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, n, wsize);
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	memset(n, 0, wsize * BYTES_PER_MPI_LIMB);
4388c2ecf20Sopenharmony_ci	cy = mpihelp_sub_n(wp, wp, ctx->p->d, wsize);
4398c2ecf20Sopenharmony_ci	mpih_set_cond(n, ctx->p->d, wsize, (cy != 0UL));
4408c2ecf20Sopenharmony_ci	mpihelp_add_n(wp, wp, n, wsize);
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic void ec_mul2_448(MPI w, MPI u, struct mpi_ec_ctx *ctx)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	ec_addm_448(w, u, u, ctx);
4468c2ecf20Sopenharmony_ci}
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_cistatic void ec_pow2_448(MPI w, const MPI b, struct mpi_ec_ctx *ctx)
4498c2ecf20Sopenharmony_ci{
4508c2ecf20Sopenharmony_ci	ec_mulm_448(w, b, b, ctx);
4518c2ecf20Sopenharmony_ci}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_cistruct field_table {
4548c2ecf20Sopenharmony_ci	const char *p;
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_ci	/* computation routines for the field.  */
4578c2ecf20Sopenharmony_ci	void (*addm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
4588c2ecf20Sopenharmony_ci	void (*subm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
4598c2ecf20Sopenharmony_ci	void (*mulm)(MPI w, MPI u, MPI v, struct mpi_ec_ctx *ctx);
4608c2ecf20Sopenharmony_ci	void (*mul2)(MPI w, MPI u, struct mpi_ec_ctx *ctx);
4618c2ecf20Sopenharmony_ci	void (*pow2)(MPI w, const MPI b, struct mpi_ec_ctx *ctx);
4628c2ecf20Sopenharmony_ci};
4638c2ecf20Sopenharmony_ci
4648c2ecf20Sopenharmony_cistatic const struct field_table field_table[] = {
4658c2ecf20Sopenharmony_ci	{
4668c2ecf20Sopenharmony_ci		"0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFED",
4678c2ecf20Sopenharmony_ci		ec_addm_25519,
4688c2ecf20Sopenharmony_ci		ec_subm_25519,
4698c2ecf20Sopenharmony_ci		ec_mulm_25519,
4708c2ecf20Sopenharmony_ci		ec_mul2_25519,
4718c2ecf20Sopenharmony_ci		ec_pow2_25519
4728c2ecf20Sopenharmony_ci	},
4738c2ecf20Sopenharmony_ci	{
4748c2ecf20Sopenharmony_ci		"0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFE"
4758c2ecf20Sopenharmony_ci		"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF",
4768c2ecf20Sopenharmony_ci		ec_addm_448,
4778c2ecf20Sopenharmony_ci		ec_subm_448,
4788c2ecf20Sopenharmony_ci		ec_mulm_448,
4798c2ecf20Sopenharmony_ci		ec_mul2_448,
4808c2ecf20Sopenharmony_ci		ec_pow2_448
4818c2ecf20Sopenharmony_ci	},
4828c2ecf20Sopenharmony_ci	{ NULL, NULL, NULL, NULL, NULL, NULL },
4838c2ecf20Sopenharmony_ci};
4848c2ecf20Sopenharmony_ci
4858c2ecf20Sopenharmony_ci/* Force recomputation of all helper variables.  */
4868c2ecf20Sopenharmony_cistatic void mpi_ec_get_reset(struct mpi_ec_ctx *ec)
4878c2ecf20Sopenharmony_ci{
4888c2ecf20Sopenharmony_ci	ec->t.valid.a_is_pminus3 = 0;
4898c2ecf20Sopenharmony_ci	ec->t.valid.two_inv_p = 0;
4908c2ecf20Sopenharmony_ci}
4918c2ecf20Sopenharmony_ci
4928c2ecf20Sopenharmony_ci/* Accessor for helper variable.  */
4938c2ecf20Sopenharmony_cistatic int ec_get_a_is_pminus3(struct mpi_ec_ctx *ec)
4948c2ecf20Sopenharmony_ci{
4958c2ecf20Sopenharmony_ci	MPI tmp;
4968c2ecf20Sopenharmony_ci
4978c2ecf20Sopenharmony_ci	if (!ec->t.valid.a_is_pminus3) {
4988c2ecf20Sopenharmony_ci		ec->t.valid.a_is_pminus3 = 1;
4998c2ecf20Sopenharmony_ci		tmp = mpi_alloc_like(ec->p);
5008c2ecf20Sopenharmony_ci		mpi_sub_ui(tmp, ec->p, 3);
5018c2ecf20Sopenharmony_ci		ec->t.a_is_pminus3 = !mpi_cmp(ec->a, tmp);
5028c2ecf20Sopenharmony_ci		mpi_free(tmp);
5038c2ecf20Sopenharmony_ci	}
5048c2ecf20Sopenharmony_ci
5058c2ecf20Sopenharmony_ci	return ec->t.a_is_pminus3;
5068c2ecf20Sopenharmony_ci}
5078c2ecf20Sopenharmony_ci
5088c2ecf20Sopenharmony_ci/* Accessor for helper variable.  */
5098c2ecf20Sopenharmony_cistatic MPI ec_get_two_inv_p(struct mpi_ec_ctx *ec)
5108c2ecf20Sopenharmony_ci{
5118c2ecf20Sopenharmony_ci	if (!ec->t.valid.two_inv_p) {
5128c2ecf20Sopenharmony_ci		ec->t.valid.two_inv_p = 1;
5138c2ecf20Sopenharmony_ci		if (!ec->t.two_inv_p)
5148c2ecf20Sopenharmony_ci			ec->t.two_inv_p = mpi_alloc(0);
5158c2ecf20Sopenharmony_ci		ec_invm(ec->t.two_inv_p, mpi_const(MPI_C_TWO), ec);
5168c2ecf20Sopenharmony_ci	}
5178c2ecf20Sopenharmony_ci	return ec->t.two_inv_p;
5188c2ecf20Sopenharmony_ci}
5198c2ecf20Sopenharmony_ci
5208c2ecf20Sopenharmony_cistatic const char *const curve25519_bad_points[] = {
5218c2ecf20Sopenharmony_ci	"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed",
5228c2ecf20Sopenharmony_ci	"0x0000000000000000000000000000000000000000000000000000000000000000",
5238c2ecf20Sopenharmony_ci	"0x0000000000000000000000000000000000000000000000000000000000000001",
5248c2ecf20Sopenharmony_ci	"0x00b8495f16056286fdb1329ceb8d09da6ac49ff1fae35616aeb8413b7c7aebe0",
5258c2ecf20Sopenharmony_ci	"0x57119fd0dd4e22d8868e1c58c45c44045bef839c55b1d0b1248c50a3bc959c5f",
5268c2ecf20Sopenharmony_ci	"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffec",
5278c2ecf20Sopenharmony_ci	"0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffee",
5288c2ecf20Sopenharmony_ci	NULL
5298c2ecf20Sopenharmony_ci};
5308c2ecf20Sopenharmony_ci
5318c2ecf20Sopenharmony_cistatic const char *const curve448_bad_points[] = {
5328c2ecf20Sopenharmony_ci	"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
5338c2ecf20Sopenharmony_ci	"ffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
5348c2ecf20Sopenharmony_ci	"0x00000000000000000000000000000000000000000000000000000000"
5358c2ecf20Sopenharmony_ci	"00000000000000000000000000000000000000000000000000000000",
5368c2ecf20Sopenharmony_ci	"0x00000000000000000000000000000000000000000000000000000000"
5378c2ecf20Sopenharmony_ci	"00000000000000000000000000000000000000000000000000000001",
5388c2ecf20Sopenharmony_ci	"0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffe"
5398c2ecf20Sopenharmony_ci	"fffffffffffffffffffffffffffffffffffffffffffffffffffffffe",
5408c2ecf20Sopenharmony_ci	"0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff"
5418c2ecf20Sopenharmony_ci	"00000000000000000000000000000000000000000000000000000000",
5428c2ecf20Sopenharmony_ci	NULL
5438c2ecf20Sopenharmony_ci};
5448c2ecf20Sopenharmony_ci
5458c2ecf20Sopenharmony_cistatic const char *const *bad_points_table[] = {
5468c2ecf20Sopenharmony_ci	curve25519_bad_points,
5478c2ecf20Sopenharmony_ci	curve448_bad_points,
5488c2ecf20Sopenharmony_ci};
5498c2ecf20Sopenharmony_ci
5508c2ecf20Sopenharmony_cistatic void mpi_ec_coefficient_normalize(MPI a, MPI p)
5518c2ecf20Sopenharmony_ci{
5528c2ecf20Sopenharmony_ci	if (a->sign) {
5538c2ecf20Sopenharmony_ci		mpi_resize(a, p->nlimbs);
5548c2ecf20Sopenharmony_ci		mpihelp_sub_n(a->d, p->d, a->d, p->nlimbs);
5558c2ecf20Sopenharmony_ci		a->nlimbs = p->nlimbs;
5568c2ecf20Sopenharmony_ci		a->sign = 0;
5578c2ecf20Sopenharmony_ci	}
5588c2ecf20Sopenharmony_ci}
5598c2ecf20Sopenharmony_ci
5608c2ecf20Sopenharmony_ci/* This function initialized a context for elliptic curve based on the
5618c2ecf20Sopenharmony_ci * field GF(p).  P is the prime specifying this field, A is the first
5628c2ecf20Sopenharmony_ci * coefficient.  CTX is expected to be zeroized.
5638c2ecf20Sopenharmony_ci */
5648c2ecf20Sopenharmony_civoid mpi_ec_init(struct mpi_ec_ctx *ctx, enum gcry_mpi_ec_models model,
5658c2ecf20Sopenharmony_ci			enum ecc_dialects dialect,
5668c2ecf20Sopenharmony_ci			int flags, MPI p, MPI a, MPI b)
5678c2ecf20Sopenharmony_ci{
5688c2ecf20Sopenharmony_ci	int i;
5698c2ecf20Sopenharmony_ci	static int use_barrett = -1 /* TODO: 1 or -1 */;
5708c2ecf20Sopenharmony_ci
5718c2ecf20Sopenharmony_ci	mpi_ec_coefficient_normalize(a, p);
5728c2ecf20Sopenharmony_ci	mpi_ec_coefficient_normalize(b, p);
5738c2ecf20Sopenharmony_ci
5748c2ecf20Sopenharmony_ci	/* Fixme: Do we want to check some constraints? e.g.  a < p  */
5758c2ecf20Sopenharmony_ci
5768c2ecf20Sopenharmony_ci	ctx->model = model;
5778c2ecf20Sopenharmony_ci	ctx->dialect = dialect;
5788c2ecf20Sopenharmony_ci	ctx->flags = flags;
5798c2ecf20Sopenharmony_ci	if (dialect == ECC_DIALECT_ED25519)
5808c2ecf20Sopenharmony_ci		ctx->nbits = 256;
5818c2ecf20Sopenharmony_ci	else
5828c2ecf20Sopenharmony_ci		ctx->nbits = mpi_get_nbits(p);
5838c2ecf20Sopenharmony_ci	ctx->p = mpi_copy(p);
5848c2ecf20Sopenharmony_ci	ctx->a = mpi_copy(a);
5858c2ecf20Sopenharmony_ci	ctx->b = mpi_copy(b);
5868c2ecf20Sopenharmony_ci
5878c2ecf20Sopenharmony_ci	ctx->d = NULL;
5888c2ecf20Sopenharmony_ci	ctx->t.two_inv_p = NULL;
5898c2ecf20Sopenharmony_ci
5908c2ecf20Sopenharmony_ci	ctx->t.p_barrett = use_barrett > 0 ? mpi_barrett_init(ctx->p, 0) : NULL;
5918c2ecf20Sopenharmony_ci
5928c2ecf20Sopenharmony_ci	mpi_ec_get_reset(ctx);
5938c2ecf20Sopenharmony_ci
5948c2ecf20Sopenharmony_ci	if (model == MPI_EC_MONTGOMERY) {
5958c2ecf20Sopenharmony_ci		for (i = 0; i < DIM(bad_points_table); i++) {
5968c2ecf20Sopenharmony_ci			MPI p_candidate = mpi_scanval(bad_points_table[i][0]);
5978c2ecf20Sopenharmony_ci			int match_p = !mpi_cmp(ctx->p, p_candidate);
5988c2ecf20Sopenharmony_ci			int j;
5998c2ecf20Sopenharmony_ci
6008c2ecf20Sopenharmony_ci			mpi_free(p_candidate);
6018c2ecf20Sopenharmony_ci			if (!match_p)
6028c2ecf20Sopenharmony_ci				continue;
6038c2ecf20Sopenharmony_ci
6048c2ecf20Sopenharmony_ci			for (j = 0; i < DIM(ctx->t.scratch) && bad_points_table[i][j]; j++)
6058c2ecf20Sopenharmony_ci				ctx->t.scratch[j] = mpi_scanval(bad_points_table[i][j]);
6068c2ecf20Sopenharmony_ci		}
6078c2ecf20Sopenharmony_ci	} else {
6088c2ecf20Sopenharmony_ci		/* Allocate scratch variables.  */
6098c2ecf20Sopenharmony_ci		for (i = 0; i < DIM(ctx->t.scratch); i++)
6108c2ecf20Sopenharmony_ci			ctx->t.scratch[i] = mpi_alloc_like(ctx->p);
6118c2ecf20Sopenharmony_ci	}
6128c2ecf20Sopenharmony_ci
6138c2ecf20Sopenharmony_ci	ctx->addm = ec_addm;
6148c2ecf20Sopenharmony_ci	ctx->subm = ec_subm;
6158c2ecf20Sopenharmony_ci	ctx->mulm = ec_mulm;
6168c2ecf20Sopenharmony_ci	ctx->mul2 = ec_mul2;
6178c2ecf20Sopenharmony_ci	ctx->pow2 = ec_pow2;
6188c2ecf20Sopenharmony_ci
6198c2ecf20Sopenharmony_ci	for (i = 0; field_table[i].p; i++) {
6208c2ecf20Sopenharmony_ci		MPI f_p;
6218c2ecf20Sopenharmony_ci
6228c2ecf20Sopenharmony_ci		f_p = mpi_scanval(field_table[i].p);
6238c2ecf20Sopenharmony_ci		if (!f_p)
6248c2ecf20Sopenharmony_ci			break;
6258c2ecf20Sopenharmony_ci
6268c2ecf20Sopenharmony_ci		if (!mpi_cmp(p, f_p)) {
6278c2ecf20Sopenharmony_ci			ctx->addm = field_table[i].addm;
6288c2ecf20Sopenharmony_ci			ctx->subm = field_table[i].subm;
6298c2ecf20Sopenharmony_ci			ctx->mulm = field_table[i].mulm;
6308c2ecf20Sopenharmony_ci			ctx->mul2 = field_table[i].mul2;
6318c2ecf20Sopenharmony_ci			ctx->pow2 = field_table[i].pow2;
6328c2ecf20Sopenharmony_ci			mpi_free(f_p);
6338c2ecf20Sopenharmony_ci
6348c2ecf20Sopenharmony_ci			mpi_resize(ctx->a, ctx->p->nlimbs);
6358c2ecf20Sopenharmony_ci			ctx->a->nlimbs = ctx->p->nlimbs;
6368c2ecf20Sopenharmony_ci
6378c2ecf20Sopenharmony_ci			mpi_resize(ctx->b, ctx->p->nlimbs);
6388c2ecf20Sopenharmony_ci			ctx->b->nlimbs = ctx->p->nlimbs;
6398c2ecf20Sopenharmony_ci
6408c2ecf20Sopenharmony_ci			for (i = 0; i < DIM(ctx->t.scratch) && ctx->t.scratch[i]; i++)
6418c2ecf20Sopenharmony_ci				ctx->t.scratch[i]->nlimbs = ctx->p->nlimbs;
6428c2ecf20Sopenharmony_ci
6438c2ecf20Sopenharmony_ci			break;
6448c2ecf20Sopenharmony_ci		}
6458c2ecf20Sopenharmony_ci
6468c2ecf20Sopenharmony_ci		mpi_free(f_p);
6478c2ecf20Sopenharmony_ci	}
6488c2ecf20Sopenharmony_ci}
6498c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_init);
6508c2ecf20Sopenharmony_ci
6518c2ecf20Sopenharmony_civoid mpi_ec_deinit(struct mpi_ec_ctx *ctx)
6528c2ecf20Sopenharmony_ci{
6538c2ecf20Sopenharmony_ci	int i;
6548c2ecf20Sopenharmony_ci
6558c2ecf20Sopenharmony_ci	mpi_barrett_free(ctx->t.p_barrett);
6568c2ecf20Sopenharmony_ci
6578c2ecf20Sopenharmony_ci	/* Domain parameter.  */
6588c2ecf20Sopenharmony_ci	mpi_free(ctx->p);
6598c2ecf20Sopenharmony_ci	mpi_free(ctx->a);
6608c2ecf20Sopenharmony_ci	mpi_free(ctx->b);
6618c2ecf20Sopenharmony_ci	mpi_point_release(ctx->G);
6628c2ecf20Sopenharmony_ci	mpi_free(ctx->n);
6638c2ecf20Sopenharmony_ci
6648c2ecf20Sopenharmony_ci	/* The key.  */
6658c2ecf20Sopenharmony_ci	mpi_point_release(ctx->Q);
6668c2ecf20Sopenharmony_ci	mpi_free(ctx->d);
6678c2ecf20Sopenharmony_ci
6688c2ecf20Sopenharmony_ci	/* Private data of ec.c.  */
6698c2ecf20Sopenharmony_ci	mpi_free(ctx->t.two_inv_p);
6708c2ecf20Sopenharmony_ci
6718c2ecf20Sopenharmony_ci	for (i = 0; i < DIM(ctx->t.scratch); i++)
6728c2ecf20Sopenharmony_ci		mpi_free(ctx->t.scratch[i]);
6738c2ecf20Sopenharmony_ci}
6748c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_deinit);
6758c2ecf20Sopenharmony_ci
6768c2ecf20Sopenharmony_ci/* Compute the affine coordinates from the projective coordinates in
6778c2ecf20Sopenharmony_ci * POINT.  Set them into X and Y.  If one coordinate is not required,
6788c2ecf20Sopenharmony_ci * X or Y may be passed as NULL.  CTX is the usual context. Returns: 0
6798c2ecf20Sopenharmony_ci * on success or !0 if POINT is at infinity.
6808c2ecf20Sopenharmony_ci */
6818c2ecf20Sopenharmony_ciint mpi_ec_get_affine(MPI x, MPI y, MPI_POINT point, struct mpi_ec_ctx *ctx)
6828c2ecf20Sopenharmony_ci{
6838c2ecf20Sopenharmony_ci	if (!mpi_cmp_ui(point->z, 0))
6848c2ecf20Sopenharmony_ci		return -1;
6858c2ecf20Sopenharmony_ci
6868c2ecf20Sopenharmony_ci	switch (ctx->model) {
6878c2ecf20Sopenharmony_ci	case MPI_EC_WEIERSTRASS: /* Using Jacobian coordinates.  */
6888c2ecf20Sopenharmony_ci		{
6898c2ecf20Sopenharmony_ci			MPI z1, z2, z3;
6908c2ecf20Sopenharmony_ci
6918c2ecf20Sopenharmony_ci			z1 = mpi_new(0);
6928c2ecf20Sopenharmony_ci			z2 = mpi_new(0);
6938c2ecf20Sopenharmony_ci			ec_invm(z1, point->z, ctx);  /* z1 = z^(-1) mod p  */
6948c2ecf20Sopenharmony_ci			ec_mulm(z2, z1, z1, ctx);    /* z2 = z^(-2) mod p  */
6958c2ecf20Sopenharmony_ci
6968c2ecf20Sopenharmony_ci			if (x)
6978c2ecf20Sopenharmony_ci				ec_mulm(x, point->x, z2, ctx);
6988c2ecf20Sopenharmony_ci
6998c2ecf20Sopenharmony_ci			if (y) {
7008c2ecf20Sopenharmony_ci				z3 = mpi_new(0);
7018c2ecf20Sopenharmony_ci				ec_mulm(z3, z2, z1, ctx);      /* z3 = z^(-3) mod p */
7028c2ecf20Sopenharmony_ci				ec_mulm(y, point->y, z3, ctx);
7038c2ecf20Sopenharmony_ci				mpi_free(z3);
7048c2ecf20Sopenharmony_ci			}
7058c2ecf20Sopenharmony_ci
7068c2ecf20Sopenharmony_ci			mpi_free(z2);
7078c2ecf20Sopenharmony_ci			mpi_free(z1);
7088c2ecf20Sopenharmony_ci		}
7098c2ecf20Sopenharmony_ci		return 0;
7108c2ecf20Sopenharmony_ci
7118c2ecf20Sopenharmony_ci	case MPI_EC_MONTGOMERY:
7128c2ecf20Sopenharmony_ci		{
7138c2ecf20Sopenharmony_ci			if (x)
7148c2ecf20Sopenharmony_ci				mpi_set(x, point->x);
7158c2ecf20Sopenharmony_ci
7168c2ecf20Sopenharmony_ci			if (y) {
7178c2ecf20Sopenharmony_ci				log_fatal("%s: Getting Y-coordinate on %s is not supported\n",
7188c2ecf20Sopenharmony_ci						"mpi_ec_get_affine", "Montgomery");
7198c2ecf20Sopenharmony_ci				return -1;
7208c2ecf20Sopenharmony_ci			}
7218c2ecf20Sopenharmony_ci		}
7228c2ecf20Sopenharmony_ci		return 0;
7238c2ecf20Sopenharmony_ci
7248c2ecf20Sopenharmony_ci	case MPI_EC_EDWARDS:
7258c2ecf20Sopenharmony_ci		{
7268c2ecf20Sopenharmony_ci			MPI z;
7278c2ecf20Sopenharmony_ci
7288c2ecf20Sopenharmony_ci			z = mpi_new(0);
7298c2ecf20Sopenharmony_ci			ec_invm(z, point->z, ctx);
7308c2ecf20Sopenharmony_ci
7318c2ecf20Sopenharmony_ci			mpi_resize(z, ctx->p->nlimbs);
7328c2ecf20Sopenharmony_ci			z->nlimbs = ctx->p->nlimbs;
7338c2ecf20Sopenharmony_ci
7348c2ecf20Sopenharmony_ci			if (x) {
7358c2ecf20Sopenharmony_ci				mpi_resize(x, ctx->p->nlimbs);
7368c2ecf20Sopenharmony_ci				x->nlimbs = ctx->p->nlimbs;
7378c2ecf20Sopenharmony_ci				ctx->mulm(x, point->x, z, ctx);
7388c2ecf20Sopenharmony_ci			}
7398c2ecf20Sopenharmony_ci			if (y) {
7408c2ecf20Sopenharmony_ci				mpi_resize(y, ctx->p->nlimbs);
7418c2ecf20Sopenharmony_ci				y->nlimbs = ctx->p->nlimbs;
7428c2ecf20Sopenharmony_ci				ctx->mulm(y, point->y, z, ctx);
7438c2ecf20Sopenharmony_ci			}
7448c2ecf20Sopenharmony_ci
7458c2ecf20Sopenharmony_ci			mpi_free(z);
7468c2ecf20Sopenharmony_ci		}
7478c2ecf20Sopenharmony_ci		return 0;
7488c2ecf20Sopenharmony_ci
7498c2ecf20Sopenharmony_ci	default:
7508c2ecf20Sopenharmony_ci		return -1;
7518c2ecf20Sopenharmony_ci	}
7528c2ecf20Sopenharmony_ci}
7538c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_get_affine);
7548c2ecf20Sopenharmony_ci
7558c2ecf20Sopenharmony_ci/*  RESULT = 2 * POINT  (Weierstrass version). */
7568c2ecf20Sopenharmony_cistatic void dup_point_weierstrass(MPI_POINT result,
7578c2ecf20Sopenharmony_ci		MPI_POINT point, struct mpi_ec_ctx *ctx)
7588c2ecf20Sopenharmony_ci{
7598c2ecf20Sopenharmony_ci#define x3 (result->x)
7608c2ecf20Sopenharmony_ci#define y3 (result->y)
7618c2ecf20Sopenharmony_ci#define z3 (result->z)
7628c2ecf20Sopenharmony_ci#define t1 (ctx->t.scratch[0])
7638c2ecf20Sopenharmony_ci#define t2 (ctx->t.scratch[1])
7648c2ecf20Sopenharmony_ci#define t3 (ctx->t.scratch[2])
7658c2ecf20Sopenharmony_ci#define l1 (ctx->t.scratch[3])
7668c2ecf20Sopenharmony_ci#define l2 (ctx->t.scratch[4])
7678c2ecf20Sopenharmony_ci#define l3 (ctx->t.scratch[5])
7688c2ecf20Sopenharmony_ci
7698c2ecf20Sopenharmony_ci	if (!mpi_cmp_ui(point->y, 0) || !mpi_cmp_ui(point->z, 0)) {
7708c2ecf20Sopenharmony_ci		/* P_y == 0 || P_z == 0 => [1:1:0] */
7718c2ecf20Sopenharmony_ci		mpi_set_ui(x3, 1);
7728c2ecf20Sopenharmony_ci		mpi_set_ui(y3, 1);
7738c2ecf20Sopenharmony_ci		mpi_set_ui(z3, 0);
7748c2ecf20Sopenharmony_ci	} else {
7758c2ecf20Sopenharmony_ci		if (ec_get_a_is_pminus3(ctx)) {
7768c2ecf20Sopenharmony_ci			/* Use the faster case.  */
7778c2ecf20Sopenharmony_ci			/* L1 = 3(X - Z^2)(X + Z^2) */
7788c2ecf20Sopenharmony_ci			/*                          T1: used for Z^2. */
7798c2ecf20Sopenharmony_ci			/*                          T2: used for the right term. */
7808c2ecf20Sopenharmony_ci			ec_pow2(t1, point->z, ctx);
7818c2ecf20Sopenharmony_ci			ec_subm(l1, point->x, t1, ctx);
7828c2ecf20Sopenharmony_ci			ec_mulm(l1, l1, mpi_const(MPI_C_THREE), ctx);
7838c2ecf20Sopenharmony_ci			ec_addm(t2, point->x, t1, ctx);
7848c2ecf20Sopenharmony_ci			ec_mulm(l1, l1, t2, ctx);
7858c2ecf20Sopenharmony_ci		} else {
7868c2ecf20Sopenharmony_ci			/* Standard case. */
7878c2ecf20Sopenharmony_ci			/* L1 = 3X^2 + aZ^4 */
7888c2ecf20Sopenharmony_ci			/*                          T1: used for aZ^4. */
7898c2ecf20Sopenharmony_ci			ec_pow2(l1, point->x, ctx);
7908c2ecf20Sopenharmony_ci			ec_mulm(l1, l1, mpi_const(MPI_C_THREE), ctx);
7918c2ecf20Sopenharmony_ci			ec_powm(t1, point->z, mpi_const(MPI_C_FOUR), ctx);
7928c2ecf20Sopenharmony_ci			ec_mulm(t1, t1, ctx->a, ctx);
7938c2ecf20Sopenharmony_ci			ec_addm(l1, l1, t1, ctx);
7948c2ecf20Sopenharmony_ci		}
7958c2ecf20Sopenharmony_ci		/* Z3 = 2YZ */
7968c2ecf20Sopenharmony_ci		ec_mulm(z3, point->y, point->z, ctx);
7978c2ecf20Sopenharmony_ci		ec_mul2(z3, z3, ctx);
7988c2ecf20Sopenharmony_ci
7998c2ecf20Sopenharmony_ci		/* L2 = 4XY^2 */
8008c2ecf20Sopenharmony_ci		/*                              T2: used for Y2; required later. */
8018c2ecf20Sopenharmony_ci		ec_pow2(t2, point->y, ctx);
8028c2ecf20Sopenharmony_ci		ec_mulm(l2, t2, point->x, ctx);
8038c2ecf20Sopenharmony_ci		ec_mulm(l2, l2, mpi_const(MPI_C_FOUR), ctx);
8048c2ecf20Sopenharmony_ci
8058c2ecf20Sopenharmony_ci		/* X3 = L1^2 - 2L2 */
8068c2ecf20Sopenharmony_ci		/*                              T1: used for L2^2. */
8078c2ecf20Sopenharmony_ci		ec_pow2(x3, l1, ctx);
8088c2ecf20Sopenharmony_ci		ec_mul2(t1, l2, ctx);
8098c2ecf20Sopenharmony_ci		ec_subm(x3, x3, t1, ctx);
8108c2ecf20Sopenharmony_ci
8118c2ecf20Sopenharmony_ci		/* L3 = 8Y^4 */
8128c2ecf20Sopenharmony_ci		/*                              T2: taken from above. */
8138c2ecf20Sopenharmony_ci		ec_pow2(t2, t2, ctx);
8148c2ecf20Sopenharmony_ci		ec_mulm(l3, t2, mpi_const(MPI_C_EIGHT), ctx);
8158c2ecf20Sopenharmony_ci
8168c2ecf20Sopenharmony_ci		/* Y3 = L1(L2 - X3) - L3 */
8178c2ecf20Sopenharmony_ci		ec_subm(y3, l2, x3, ctx);
8188c2ecf20Sopenharmony_ci		ec_mulm(y3, y3, l1, ctx);
8198c2ecf20Sopenharmony_ci		ec_subm(y3, y3, l3, ctx);
8208c2ecf20Sopenharmony_ci	}
8218c2ecf20Sopenharmony_ci
8228c2ecf20Sopenharmony_ci#undef x3
8238c2ecf20Sopenharmony_ci#undef y3
8248c2ecf20Sopenharmony_ci#undef z3
8258c2ecf20Sopenharmony_ci#undef t1
8268c2ecf20Sopenharmony_ci#undef t2
8278c2ecf20Sopenharmony_ci#undef t3
8288c2ecf20Sopenharmony_ci#undef l1
8298c2ecf20Sopenharmony_ci#undef l2
8308c2ecf20Sopenharmony_ci#undef l3
8318c2ecf20Sopenharmony_ci}
8328c2ecf20Sopenharmony_ci
8338c2ecf20Sopenharmony_ci/*  RESULT = 2 * POINT  (Montgomery version). */
8348c2ecf20Sopenharmony_cistatic void dup_point_montgomery(MPI_POINT result,
8358c2ecf20Sopenharmony_ci				MPI_POINT point, struct mpi_ec_ctx *ctx)
8368c2ecf20Sopenharmony_ci{
8378c2ecf20Sopenharmony_ci	(void)result;
8388c2ecf20Sopenharmony_ci	(void)point;
8398c2ecf20Sopenharmony_ci	(void)ctx;
8408c2ecf20Sopenharmony_ci	log_fatal("%s: %s not yet supported\n",
8418c2ecf20Sopenharmony_ci			"mpi_ec_dup_point", "Montgomery");
8428c2ecf20Sopenharmony_ci}
8438c2ecf20Sopenharmony_ci
8448c2ecf20Sopenharmony_ci/*  RESULT = 2 * POINT  (Twisted Edwards version). */
8458c2ecf20Sopenharmony_cistatic void dup_point_edwards(MPI_POINT result,
8468c2ecf20Sopenharmony_ci		MPI_POINT point, struct mpi_ec_ctx *ctx)
8478c2ecf20Sopenharmony_ci{
8488c2ecf20Sopenharmony_ci#define X1 (point->x)
8498c2ecf20Sopenharmony_ci#define Y1 (point->y)
8508c2ecf20Sopenharmony_ci#define Z1 (point->z)
8518c2ecf20Sopenharmony_ci#define X3 (result->x)
8528c2ecf20Sopenharmony_ci#define Y3 (result->y)
8538c2ecf20Sopenharmony_ci#define Z3 (result->z)
8548c2ecf20Sopenharmony_ci#define B (ctx->t.scratch[0])
8558c2ecf20Sopenharmony_ci#define C (ctx->t.scratch[1])
8568c2ecf20Sopenharmony_ci#define D (ctx->t.scratch[2])
8578c2ecf20Sopenharmony_ci#define E (ctx->t.scratch[3])
8588c2ecf20Sopenharmony_ci#define F (ctx->t.scratch[4])
8598c2ecf20Sopenharmony_ci#define H (ctx->t.scratch[5])
8608c2ecf20Sopenharmony_ci#define J (ctx->t.scratch[6])
8618c2ecf20Sopenharmony_ci
8628c2ecf20Sopenharmony_ci	/* Compute: (X_3 : Y_3 : Z_3) = 2( X_1 : Y_1 : Z_1 ) */
8638c2ecf20Sopenharmony_ci
8648c2ecf20Sopenharmony_ci	/* B = (X_1 + Y_1)^2  */
8658c2ecf20Sopenharmony_ci	ctx->addm(B, X1, Y1, ctx);
8668c2ecf20Sopenharmony_ci	ctx->pow2(B, B, ctx);
8678c2ecf20Sopenharmony_ci
8688c2ecf20Sopenharmony_ci	/* C = X_1^2 */
8698c2ecf20Sopenharmony_ci	/* D = Y_1^2 */
8708c2ecf20Sopenharmony_ci	ctx->pow2(C, X1, ctx);
8718c2ecf20Sopenharmony_ci	ctx->pow2(D, Y1, ctx);
8728c2ecf20Sopenharmony_ci
8738c2ecf20Sopenharmony_ci	/* E = aC */
8748c2ecf20Sopenharmony_ci	if (ctx->dialect == ECC_DIALECT_ED25519)
8758c2ecf20Sopenharmony_ci		ctx->subm(E, ctx->p, C, ctx);
8768c2ecf20Sopenharmony_ci	else
8778c2ecf20Sopenharmony_ci		ctx->mulm(E, ctx->a, C, ctx);
8788c2ecf20Sopenharmony_ci
8798c2ecf20Sopenharmony_ci	/* F = E + D */
8808c2ecf20Sopenharmony_ci	ctx->addm(F, E, D, ctx);
8818c2ecf20Sopenharmony_ci
8828c2ecf20Sopenharmony_ci	/* H = Z_1^2 */
8838c2ecf20Sopenharmony_ci	ctx->pow2(H, Z1, ctx);
8848c2ecf20Sopenharmony_ci
8858c2ecf20Sopenharmony_ci	/* J = F - 2H */
8868c2ecf20Sopenharmony_ci	ctx->mul2(J, H, ctx);
8878c2ecf20Sopenharmony_ci	ctx->subm(J, F, J, ctx);
8888c2ecf20Sopenharmony_ci
8898c2ecf20Sopenharmony_ci	/* X_3 = (B - C - D) · J */
8908c2ecf20Sopenharmony_ci	ctx->subm(X3, B, C, ctx);
8918c2ecf20Sopenharmony_ci	ctx->subm(X3, X3, D, ctx);
8928c2ecf20Sopenharmony_ci	ctx->mulm(X3, X3, J, ctx);
8938c2ecf20Sopenharmony_ci
8948c2ecf20Sopenharmony_ci	/* Y_3 = F · (E - D) */
8958c2ecf20Sopenharmony_ci	ctx->subm(Y3, E, D, ctx);
8968c2ecf20Sopenharmony_ci	ctx->mulm(Y3, Y3, F, ctx);
8978c2ecf20Sopenharmony_ci
8988c2ecf20Sopenharmony_ci	/* Z_3 = F · J */
8998c2ecf20Sopenharmony_ci	ctx->mulm(Z3, F, J, ctx);
9008c2ecf20Sopenharmony_ci
9018c2ecf20Sopenharmony_ci#undef X1
9028c2ecf20Sopenharmony_ci#undef Y1
9038c2ecf20Sopenharmony_ci#undef Z1
9048c2ecf20Sopenharmony_ci#undef X3
9058c2ecf20Sopenharmony_ci#undef Y3
9068c2ecf20Sopenharmony_ci#undef Z3
9078c2ecf20Sopenharmony_ci#undef B
9088c2ecf20Sopenharmony_ci#undef C
9098c2ecf20Sopenharmony_ci#undef D
9108c2ecf20Sopenharmony_ci#undef E
9118c2ecf20Sopenharmony_ci#undef F
9128c2ecf20Sopenharmony_ci#undef H
9138c2ecf20Sopenharmony_ci#undef J
9148c2ecf20Sopenharmony_ci}
9158c2ecf20Sopenharmony_ci
9168c2ecf20Sopenharmony_ci/*  RESULT = 2 * POINT  */
9178c2ecf20Sopenharmony_cistatic void
9188c2ecf20Sopenharmony_cimpi_ec_dup_point(MPI_POINT result, MPI_POINT point, struct mpi_ec_ctx *ctx)
9198c2ecf20Sopenharmony_ci{
9208c2ecf20Sopenharmony_ci	switch (ctx->model) {
9218c2ecf20Sopenharmony_ci	case MPI_EC_WEIERSTRASS:
9228c2ecf20Sopenharmony_ci		dup_point_weierstrass(result, point, ctx);
9238c2ecf20Sopenharmony_ci		break;
9248c2ecf20Sopenharmony_ci	case MPI_EC_MONTGOMERY:
9258c2ecf20Sopenharmony_ci		dup_point_montgomery(result, point, ctx);
9268c2ecf20Sopenharmony_ci		break;
9278c2ecf20Sopenharmony_ci	case MPI_EC_EDWARDS:
9288c2ecf20Sopenharmony_ci		dup_point_edwards(result, point, ctx);
9298c2ecf20Sopenharmony_ci		break;
9308c2ecf20Sopenharmony_ci	}
9318c2ecf20Sopenharmony_ci}
9328c2ecf20Sopenharmony_ci
9338c2ecf20Sopenharmony_ci/* RESULT = P1 + P2  (Weierstrass version).*/
9348c2ecf20Sopenharmony_cistatic void add_points_weierstrass(MPI_POINT result,
9358c2ecf20Sopenharmony_ci		MPI_POINT p1, MPI_POINT p2,
9368c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
9378c2ecf20Sopenharmony_ci{
9388c2ecf20Sopenharmony_ci#define x1 (p1->x)
9398c2ecf20Sopenharmony_ci#define y1 (p1->y)
9408c2ecf20Sopenharmony_ci#define z1 (p1->z)
9418c2ecf20Sopenharmony_ci#define x2 (p2->x)
9428c2ecf20Sopenharmony_ci#define y2 (p2->y)
9438c2ecf20Sopenharmony_ci#define z2 (p2->z)
9448c2ecf20Sopenharmony_ci#define x3 (result->x)
9458c2ecf20Sopenharmony_ci#define y3 (result->y)
9468c2ecf20Sopenharmony_ci#define z3 (result->z)
9478c2ecf20Sopenharmony_ci#define l1 (ctx->t.scratch[0])
9488c2ecf20Sopenharmony_ci#define l2 (ctx->t.scratch[1])
9498c2ecf20Sopenharmony_ci#define l3 (ctx->t.scratch[2])
9508c2ecf20Sopenharmony_ci#define l4 (ctx->t.scratch[3])
9518c2ecf20Sopenharmony_ci#define l5 (ctx->t.scratch[4])
9528c2ecf20Sopenharmony_ci#define l6 (ctx->t.scratch[5])
9538c2ecf20Sopenharmony_ci#define l7 (ctx->t.scratch[6])
9548c2ecf20Sopenharmony_ci#define l8 (ctx->t.scratch[7])
9558c2ecf20Sopenharmony_ci#define l9 (ctx->t.scratch[8])
9568c2ecf20Sopenharmony_ci#define t1 (ctx->t.scratch[9])
9578c2ecf20Sopenharmony_ci#define t2 (ctx->t.scratch[10])
9588c2ecf20Sopenharmony_ci
9598c2ecf20Sopenharmony_ci	if ((!mpi_cmp(x1, x2)) && (!mpi_cmp(y1, y2)) && (!mpi_cmp(z1, z2))) {
9608c2ecf20Sopenharmony_ci		/* Same point; need to call the duplicate function.  */
9618c2ecf20Sopenharmony_ci		mpi_ec_dup_point(result, p1, ctx);
9628c2ecf20Sopenharmony_ci	} else if (!mpi_cmp_ui(z1, 0)) {
9638c2ecf20Sopenharmony_ci		/* P1 is at infinity.  */
9648c2ecf20Sopenharmony_ci		mpi_set(x3, p2->x);
9658c2ecf20Sopenharmony_ci		mpi_set(y3, p2->y);
9668c2ecf20Sopenharmony_ci		mpi_set(z3, p2->z);
9678c2ecf20Sopenharmony_ci	} else if (!mpi_cmp_ui(z2, 0)) {
9688c2ecf20Sopenharmony_ci		/* P2 is at infinity.  */
9698c2ecf20Sopenharmony_ci		mpi_set(x3, p1->x);
9708c2ecf20Sopenharmony_ci		mpi_set(y3, p1->y);
9718c2ecf20Sopenharmony_ci		mpi_set(z3, p1->z);
9728c2ecf20Sopenharmony_ci	} else {
9738c2ecf20Sopenharmony_ci		int z1_is_one = !mpi_cmp_ui(z1, 1);
9748c2ecf20Sopenharmony_ci		int z2_is_one = !mpi_cmp_ui(z2, 1);
9758c2ecf20Sopenharmony_ci
9768c2ecf20Sopenharmony_ci		/* l1 = x1 z2^2  */
9778c2ecf20Sopenharmony_ci		/* l2 = x2 z1^2  */
9788c2ecf20Sopenharmony_ci		if (z2_is_one)
9798c2ecf20Sopenharmony_ci			mpi_set(l1, x1);
9808c2ecf20Sopenharmony_ci		else {
9818c2ecf20Sopenharmony_ci			ec_pow2(l1, z2, ctx);
9828c2ecf20Sopenharmony_ci			ec_mulm(l1, l1, x1, ctx);
9838c2ecf20Sopenharmony_ci		}
9848c2ecf20Sopenharmony_ci		if (z1_is_one)
9858c2ecf20Sopenharmony_ci			mpi_set(l2, x2);
9868c2ecf20Sopenharmony_ci		else {
9878c2ecf20Sopenharmony_ci			ec_pow2(l2, z1, ctx);
9888c2ecf20Sopenharmony_ci			ec_mulm(l2, l2, x2, ctx);
9898c2ecf20Sopenharmony_ci		}
9908c2ecf20Sopenharmony_ci		/* l3 = l1 - l2 */
9918c2ecf20Sopenharmony_ci		ec_subm(l3, l1, l2, ctx);
9928c2ecf20Sopenharmony_ci		/* l4 = y1 z2^3  */
9938c2ecf20Sopenharmony_ci		ec_powm(l4, z2, mpi_const(MPI_C_THREE), ctx);
9948c2ecf20Sopenharmony_ci		ec_mulm(l4, l4, y1, ctx);
9958c2ecf20Sopenharmony_ci		/* l5 = y2 z1^3  */
9968c2ecf20Sopenharmony_ci		ec_powm(l5, z1, mpi_const(MPI_C_THREE), ctx);
9978c2ecf20Sopenharmony_ci		ec_mulm(l5, l5, y2, ctx);
9988c2ecf20Sopenharmony_ci		/* l6 = l4 - l5  */
9998c2ecf20Sopenharmony_ci		ec_subm(l6, l4, l5, ctx);
10008c2ecf20Sopenharmony_ci
10018c2ecf20Sopenharmony_ci		if (!mpi_cmp_ui(l3, 0)) {
10028c2ecf20Sopenharmony_ci			if (!mpi_cmp_ui(l6, 0)) {
10038c2ecf20Sopenharmony_ci				/* P1 and P2 are the same - use duplicate function. */
10048c2ecf20Sopenharmony_ci				mpi_ec_dup_point(result, p1, ctx);
10058c2ecf20Sopenharmony_ci			} else {
10068c2ecf20Sopenharmony_ci				/* P1 is the inverse of P2.  */
10078c2ecf20Sopenharmony_ci				mpi_set_ui(x3, 1);
10088c2ecf20Sopenharmony_ci				mpi_set_ui(y3, 1);
10098c2ecf20Sopenharmony_ci				mpi_set_ui(z3, 0);
10108c2ecf20Sopenharmony_ci			}
10118c2ecf20Sopenharmony_ci		} else {
10128c2ecf20Sopenharmony_ci			/* l7 = l1 + l2  */
10138c2ecf20Sopenharmony_ci			ec_addm(l7, l1, l2, ctx);
10148c2ecf20Sopenharmony_ci			/* l8 = l4 + l5  */
10158c2ecf20Sopenharmony_ci			ec_addm(l8, l4, l5, ctx);
10168c2ecf20Sopenharmony_ci			/* z3 = z1 z2 l3  */
10178c2ecf20Sopenharmony_ci			ec_mulm(z3, z1, z2, ctx);
10188c2ecf20Sopenharmony_ci			ec_mulm(z3, z3, l3, ctx);
10198c2ecf20Sopenharmony_ci			/* x3 = l6^2 - l7 l3^2  */
10208c2ecf20Sopenharmony_ci			ec_pow2(t1, l6, ctx);
10218c2ecf20Sopenharmony_ci			ec_pow2(t2, l3, ctx);
10228c2ecf20Sopenharmony_ci			ec_mulm(t2, t2, l7, ctx);
10238c2ecf20Sopenharmony_ci			ec_subm(x3, t1, t2, ctx);
10248c2ecf20Sopenharmony_ci			/* l9 = l7 l3^2 - 2 x3  */
10258c2ecf20Sopenharmony_ci			ec_mul2(t1, x3, ctx);
10268c2ecf20Sopenharmony_ci			ec_subm(l9, t2, t1, ctx);
10278c2ecf20Sopenharmony_ci			/* y3 = (l9 l6 - l8 l3^3)/2  */
10288c2ecf20Sopenharmony_ci			ec_mulm(l9, l9, l6, ctx);
10298c2ecf20Sopenharmony_ci			ec_powm(t1, l3, mpi_const(MPI_C_THREE), ctx); /* fixme: Use saved value*/
10308c2ecf20Sopenharmony_ci			ec_mulm(t1, t1, l8, ctx);
10318c2ecf20Sopenharmony_ci			ec_subm(y3, l9, t1, ctx);
10328c2ecf20Sopenharmony_ci			ec_mulm(y3, y3, ec_get_two_inv_p(ctx), ctx);
10338c2ecf20Sopenharmony_ci		}
10348c2ecf20Sopenharmony_ci	}
10358c2ecf20Sopenharmony_ci
10368c2ecf20Sopenharmony_ci#undef x1
10378c2ecf20Sopenharmony_ci#undef y1
10388c2ecf20Sopenharmony_ci#undef z1
10398c2ecf20Sopenharmony_ci#undef x2
10408c2ecf20Sopenharmony_ci#undef y2
10418c2ecf20Sopenharmony_ci#undef z2
10428c2ecf20Sopenharmony_ci#undef x3
10438c2ecf20Sopenharmony_ci#undef y3
10448c2ecf20Sopenharmony_ci#undef z3
10458c2ecf20Sopenharmony_ci#undef l1
10468c2ecf20Sopenharmony_ci#undef l2
10478c2ecf20Sopenharmony_ci#undef l3
10488c2ecf20Sopenharmony_ci#undef l4
10498c2ecf20Sopenharmony_ci#undef l5
10508c2ecf20Sopenharmony_ci#undef l6
10518c2ecf20Sopenharmony_ci#undef l7
10528c2ecf20Sopenharmony_ci#undef l8
10538c2ecf20Sopenharmony_ci#undef l9
10548c2ecf20Sopenharmony_ci#undef t1
10558c2ecf20Sopenharmony_ci#undef t2
10568c2ecf20Sopenharmony_ci}
10578c2ecf20Sopenharmony_ci
10588c2ecf20Sopenharmony_ci/* RESULT = P1 + P2  (Montgomery version).*/
10598c2ecf20Sopenharmony_cistatic void add_points_montgomery(MPI_POINT result,
10608c2ecf20Sopenharmony_ci		MPI_POINT p1, MPI_POINT p2,
10618c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
10628c2ecf20Sopenharmony_ci{
10638c2ecf20Sopenharmony_ci	(void)result;
10648c2ecf20Sopenharmony_ci	(void)p1;
10658c2ecf20Sopenharmony_ci	(void)p2;
10668c2ecf20Sopenharmony_ci	(void)ctx;
10678c2ecf20Sopenharmony_ci	log_fatal("%s: %s not yet supported\n",
10688c2ecf20Sopenharmony_ci			"mpi_ec_add_points", "Montgomery");
10698c2ecf20Sopenharmony_ci}
10708c2ecf20Sopenharmony_ci
10718c2ecf20Sopenharmony_ci/* RESULT = P1 + P2  (Twisted Edwards version).*/
10728c2ecf20Sopenharmony_cistatic void add_points_edwards(MPI_POINT result,
10738c2ecf20Sopenharmony_ci		MPI_POINT p1, MPI_POINT p2,
10748c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
10758c2ecf20Sopenharmony_ci{
10768c2ecf20Sopenharmony_ci#define X1 (p1->x)
10778c2ecf20Sopenharmony_ci#define Y1 (p1->y)
10788c2ecf20Sopenharmony_ci#define Z1 (p1->z)
10798c2ecf20Sopenharmony_ci#define X2 (p2->x)
10808c2ecf20Sopenharmony_ci#define Y2 (p2->y)
10818c2ecf20Sopenharmony_ci#define Z2 (p2->z)
10828c2ecf20Sopenharmony_ci#define X3 (result->x)
10838c2ecf20Sopenharmony_ci#define Y3 (result->y)
10848c2ecf20Sopenharmony_ci#define Z3 (result->z)
10858c2ecf20Sopenharmony_ci#define A (ctx->t.scratch[0])
10868c2ecf20Sopenharmony_ci#define B (ctx->t.scratch[1])
10878c2ecf20Sopenharmony_ci#define C (ctx->t.scratch[2])
10888c2ecf20Sopenharmony_ci#define D (ctx->t.scratch[3])
10898c2ecf20Sopenharmony_ci#define E (ctx->t.scratch[4])
10908c2ecf20Sopenharmony_ci#define F (ctx->t.scratch[5])
10918c2ecf20Sopenharmony_ci#define G (ctx->t.scratch[6])
10928c2ecf20Sopenharmony_ci#define tmp (ctx->t.scratch[7])
10938c2ecf20Sopenharmony_ci
10948c2ecf20Sopenharmony_ci	point_resize(result, ctx);
10958c2ecf20Sopenharmony_ci
10968c2ecf20Sopenharmony_ci	/* Compute: (X_3 : Y_3 : Z_3) = (X_1 : Y_1 : Z_1) + (X_2 : Y_2 : Z_3) */
10978c2ecf20Sopenharmony_ci
10988c2ecf20Sopenharmony_ci	/* A = Z1 · Z2 */
10998c2ecf20Sopenharmony_ci	ctx->mulm(A, Z1, Z2, ctx);
11008c2ecf20Sopenharmony_ci
11018c2ecf20Sopenharmony_ci	/* B = A^2 */
11028c2ecf20Sopenharmony_ci	ctx->pow2(B, A, ctx);
11038c2ecf20Sopenharmony_ci
11048c2ecf20Sopenharmony_ci	/* C = X1 · X2 */
11058c2ecf20Sopenharmony_ci	ctx->mulm(C, X1, X2, ctx);
11068c2ecf20Sopenharmony_ci
11078c2ecf20Sopenharmony_ci	/* D = Y1 · Y2 */
11088c2ecf20Sopenharmony_ci	ctx->mulm(D, Y1, Y2, ctx);
11098c2ecf20Sopenharmony_ci
11108c2ecf20Sopenharmony_ci	/* E = d · C · D */
11118c2ecf20Sopenharmony_ci	ctx->mulm(E, ctx->b, C, ctx);
11128c2ecf20Sopenharmony_ci	ctx->mulm(E, E, D, ctx);
11138c2ecf20Sopenharmony_ci
11148c2ecf20Sopenharmony_ci	/* F = B - E */
11158c2ecf20Sopenharmony_ci	ctx->subm(F, B, E, ctx);
11168c2ecf20Sopenharmony_ci
11178c2ecf20Sopenharmony_ci	/* G = B + E */
11188c2ecf20Sopenharmony_ci	ctx->addm(G, B, E, ctx);
11198c2ecf20Sopenharmony_ci
11208c2ecf20Sopenharmony_ci	/* X_3 = A · F · ((X_1 + Y_1) · (X_2 + Y_2) - C - D) */
11218c2ecf20Sopenharmony_ci	ctx->addm(tmp, X1, Y1, ctx);
11228c2ecf20Sopenharmony_ci	ctx->addm(X3, X2, Y2, ctx);
11238c2ecf20Sopenharmony_ci	ctx->mulm(X3, X3, tmp, ctx);
11248c2ecf20Sopenharmony_ci	ctx->subm(X3, X3, C, ctx);
11258c2ecf20Sopenharmony_ci	ctx->subm(X3, X3, D, ctx);
11268c2ecf20Sopenharmony_ci	ctx->mulm(X3, X3, F, ctx);
11278c2ecf20Sopenharmony_ci	ctx->mulm(X3, X3, A, ctx);
11288c2ecf20Sopenharmony_ci
11298c2ecf20Sopenharmony_ci	/* Y_3 = A · G · (D - aC) */
11308c2ecf20Sopenharmony_ci	if (ctx->dialect == ECC_DIALECT_ED25519) {
11318c2ecf20Sopenharmony_ci		ctx->addm(Y3, D, C, ctx);
11328c2ecf20Sopenharmony_ci	} else {
11338c2ecf20Sopenharmony_ci		ctx->mulm(Y3, ctx->a, C, ctx);
11348c2ecf20Sopenharmony_ci		ctx->subm(Y3, D, Y3, ctx);
11358c2ecf20Sopenharmony_ci	}
11368c2ecf20Sopenharmony_ci	ctx->mulm(Y3, Y3, G, ctx);
11378c2ecf20Sopenharmony_ci	ctx->mulm(Y3, Y3, A, ctx);
11388c2ecf20Sopenharmony_ci
11398c2ecf20Sopenharmony_ci	/* Z_3 = F · G */
11408c2ecf20Sopenharmony_ci	ctx->mulm(Z3, F, G, ctx);
11418c2ecf20Sopenharmony_ci
11428c2ecf20Sopenharmony_ci
11438c2ecf20Sopenharmony_ci#undef X1
11448c2ecf20Sopenharmony_ci#undef Y1
11458c2ecf20Sopenharmony_ci#undef Z1
11468c2ecf20Sopenharmony_ci#undef X2
11478c2ecf20Sopenharmony_ci#undef Y2
11488c2ecf20Sopenharmony_ci#undef Z2
11498c2ecf20Sopenharmony_ci#undef X3
11508c2ecf20Sopenharmony_ci#undef Y3
11518c2ecf20Sopenharmony_ci#undef Z3
11528c2ecf20Sopenharmony_ci#undef A
11538c2ecf20Sopenharmony_ci#undef B
11548c2ecf20Sopenharmony_ci#undef C
11558c2ecf20Sopenharmony_ci#undef D
11568c2ecf20Sopenharmony_ci#undef E
11578c2ecf20Sopenharmony_ci#undef F
11588c2ecf20Sopenharmony_ci#undef G
11598c2ecf20Sopenharmony_ci#undef tmp
11608c2ecf20Sopenharmony_ci}
11618c2ecf20Sopenharmony_ci
11628c2ecf20Sopenharmony_ci/* Compute a step of Montgomery Ladder (only use X and Z in the point).
11638c2ecf20Sopenharmony_ci * Inputs:  P1, P2, and x-coordinate of DIF = P1 - P1.
11648c2ecf20Sopenharmony_ci * Outputs: PRD = 2 * P1 and  SUM = P1 + P2.
11658c2ecf20Sopenharmony_ci */
11668c2ecf20Sopenharmony_cistatic void montgomery_ladder(MPI_POINT prd, MPI_POINT sum,
11678c2ecf20Sopenharmony_ci		MPI_POINT p1, MPI_POINT p2, MPI dif_x,
11688c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
11698c2ecf20Sopenharmony_ci{
11708c2ecf20Sopenharmony_ci	ctx->addm(sum->x, p2->x, p2->z, ctx);
11718c2ecf20Sopenharmony_ci	ctx->subm(p2->z, p2->x, p2->z, ctx);
11728c2ecf20Sopenharmony_ci	ctx->addm(prd->x, p1->x, p1->z, ctx);
11738c2ecf20Sopenharmony_ci	ctx->subm(p1->z, p1->x, p1->z, ctx);
11748c2ecf20Sopenharmony_ci	ctx->mulm(p2->x, p1->z, sum->x, ctx);
11758c2ecf20Sopenharmony_ci	ctx->mulm(p2->z, prd->x, p2->z, ctx);
11768c2ecf20Sopenharmony_ci	ctx->pow2(p1->x, prd->x, ctx);
11778c2ecf20Sopenharmony_ci	ctx->pow2(p1->z, p1->z, ctx);
11788c2ecf20Sopenharmony_ci	ctx->addm(sum->x, p2->x, p2->z, ctx);
11798c2ecf20Sopenharmony_ci	ctx->subm(p2->z, p2->x, p2->z, ctx);
11808c2ecf20Sopenharmony_ci	ctx->mulm(prd->x, p1->x, p1->z, ctx);
11818c2ecf20Sopenharmony_ci	ctx->subm(p1->z, p1->x, p1->z, ctx);
11828c2ecf20Sopenharmony_ci	ctx->pow2(sum->x, sum->x, ctx);
11838c2ecf20Sopenharmony_ci	ctx->pow2(sum->z, p2->z, ctx);
11848c2ecf20Sopenharmony_ci	ctx->mulm(prd->z, p1->z, ctx->a, ctx); /* CTX->A: (a-2)/4 */
11858c2ecf20Sopenharmony_ci	ctx->mulm(sum->z, sum->z, dif_x, ctx);
11868c2ecf20Sopenharmony_ci	ctx->addm(prd->z, p1->x, prd->z, ctx);
11878c2ecf20Sopenharmony_ci	ctx->mulm(prd->z, prd->z, p1->z, ctx);
11888c2ecf20Sopenharmony_ci}
11898c2ecf20Sopenharmony_ci
11908c2ecf20Sopenharmony_ci/* RESULT = P1 + P2 */
11918c2ecf20Sopenharmony_civoid mpi_ec_add_points(MPI_POINT result,
11928c2ecf20Sopenharmony_ci		MPI_POINT p1, MPI_POINT p2,
11938c2ecf20Sopenharmony_ci		struct mpi_ec_ctx *ctx)
11948c2ecf20Sopenharmony_ci{
11958c2ecf20Sopenharmony_ci	switch (ctx->model) {
11968c2ecf20Sopenharmony_ci	case MPI_EC_WEIERSTRASS:
11978c2ecf20Sopenharmony_ci		add_points_weierstrass(result, p1, p2, ctx);
11988c2ecf20Sopenharmony_ci		break;
11998c2ecf20Sopenharmony_ci	case MPI_EC_MONTGOMERY:
12008c2ecf20Sopenharmony_ci		add_points_montgomery(result, p1, p2, ctx);
12018c2ecf20Sopenharmony_ci		break;
12028c2ecf20Sopenharmony_ci	case MPI_EC_EDWARDS:
12038c2ecf20Sopenharmony_ci		add_points_edwards(result, p1, p2, ctx);
12048c2ecf20Sopenharmony_ci		break;
12058c2ecf20Sopenharmony_ci	}
12068c2ecf20Sopenharmony_ci}
12078c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_add_points);
12088c2ecf20Sopenharmony_ci
12098c2ecf20Sopenharmony_ci/* Scalar point multiplication - the main function for ECC.  If takes
12108c2ecf20Sopenharmony_ci * an integer SCALAR and a POINT as well as the usual context CTX.
12118c2ecf20Sopenharmony_ci * RESULT will be set to the resulting point.
12128c2ecf20Sopenharmony_ci */
12138c2ecf20Sopenharmony_civoid mpi_ec_mul_point(MPI_POINT result,
12148c2ecf20Sopenharmony_ci			MPI scalar, MPI_POINT point,
12158c2ecf20Sopenharmony_ci			struct mpi_ec_ctx *ctx)
12168c2ecf20Sopenharmony_ci{
12178c2ecf20Sopenharmony_ci	MPI x1, y1, z1, k, h, yy;
12188c2ecf20Sopenharmony_ci	unsigned int i, loops;
12198c2ecf20Sopenharmony_ci	struct gcry_mpi_point p1, p2, p1inv;
12208c2ecf20Sopenharmony_ci
12218c2ecf20Sopenharmony_ci	if (ctx->model == MPI_EC_EDWARDS) {
12228c2ecf20Sopenharmony_ci		/* Simple left to right binary method.  Algorithm 3.27 from
12238c2ecf20Sopenharmony_ci		 * {author={Hankerson, Darrel and Menezes, Alfred J. and Vanstone, Scott},
12248c2ecf20Sopenharmony_ci		 *  title = {Guide to Elliptic Curve Cryptography},
12258c2ecf20Sopenharmony_ci		 *  year = {2003}, isbn = {038795273X},
12268c2ecf20Sopenharmony_ci		 *  url = {http://www.cacr.math.uwaterloo.ca/ecc/},
12278c2ecf20Sopenharmony_ci		 *  publisher = {Springer-Verlag New York, Inc.}}
12288c2ecf20Sopenharmony_ci		 */
12298c2ecf20Sopenharmony_ci		unsigned int nbits;
12308c2ecf20Sopenharmony_ci		int j;
12318c2ecf20Sopenharmony_ci
12328c2ecf20Sopenharmony_ci		if (mpi_cmp(scalar, ctx->p) >= 0)
12338c2ecf20Sopenharmony_ci			nbits = mpi_get_nbits(scalar);
12348c2ecf20Sopenharmony_ci		else
12358c2ecf20Sopenharmony_ci			nbits = mpi_get_nbits(ctx->p);
12368c2ecf20Sopenharmony_ci
12378c2ecf20Sopenharmony_ci		mpi_set_ui(result->x, 0);
12388c2ecf20Sopenharmony_ci		mpi_set_ui(result->y, 1);
12398c2ecf20Sopenharmony_ci		mpi_set_ui(result->z, 1);
12408c2ecf20Sopenharmony_ci		point_resize(point, ctx);
12418c2ecf20Sopenharmony_ci
12428c2ecf20Sopenharmony_ci		point_resize(result, ctx);
12438c2ecf20Sopenharmony_ci		point_resize(point, ctx);
12448c2ecf20Sopenharmony_ci
12458c2ecf20Sopenharmony_ci		for (j = nbits-1; j >= 0; j--) {
12468c2ecf20Sopenharmony_ci			mpi_ec_dup_point(result, result, ctx);
12478c2ecf20Sopenharmony_ci			if (mpi_test_bit(scalar, j))
12488c2ecf20Sopenharmony_ci				mpi_ec_add_points(result, result, point, ctx);
12498c2ecf20Sopenharmony_ci		}
12508c2ecf20Sopenharmony_ci		return;
12518c2ecf20Sopenharmony_ci	} else if (ctx->model == MPI_EC_MONTGOMERY) {
12528c2ecf20Sopenharmony_ci		unsigned int nbits;
12538c2ecf20Sopenharmony_ci		int j;
12548c2ecf20Sopenharmony_ci		struct gcry_mpi_point p1_, p2_;
12558c2ecf20Sopenharmony_ci		MPI_POINT q1, q2, prd, sum;
12568c2ecf20Sopenharmony_ci		unsigned long sw;
12578c2ecf20Sopenharmony_ci		mpi_size_t rsize;
12588c2ecf20Sopenharmony_ci		int scalar_copied = 0;
12598c2ecf20Sopenharmony_ci
12608c2ecf20Sopenharmony_ci		/* Compute scalar point multiplication with Montgomery Ladder.
12618c2ecf20Sopenharmony_ci		 * Note that we don't use Y-coordinate in the points at all.
12628c2ecf20Sopenharmony_ci		 * RESULT->Y will be filled by zero.
12638c2ecf20Sopenharmony_ci		 */
12648c2ecf20Sopenharmony_ci
12658c2ecf20Sopenharmony_ci		nbits = mpi_get_nbits(scalar);
12668c2ecf20Sopenharmony_ci		point_init(&p1);
12678c2ecf20Sopenharmony_ci		point_init(&p2);
12688c2ecf20Sopenharmony_ci		point_init(&p1_);
12698c2ecf20Sopenharmony_ci		point_init(&p2_);
12708c2ecf20Sopenharmony_ci		mpi_set_ui(p1.x, 1);
12718c2ecf20Sopenharmony_ci		mpi_free(p2.x);
12728c2ecf20Sopenharmony_ci		p2.x = mpi_copy(point->x);
12738c2ecf20Sopenharmony_ci		mpi_set_ui(p2.z, 1);
12748c2ecf20Sopenharmony_ci
12758c2ecf20Sopenharmony_ci		point_resize(&p1, ctx);
12768c2ecf20Sopenharmony_ci		point_resize(&p2, ctx);
12778c2ecf20Sopenharmony_ci		point_resize(&p1_, ctx);
12788c2ecf20Sopenharmony_ci		point_resize(&p2_, ctx);
12798c2ecf20Sopenharmony_ci
12808c2ecf20Sopenharmony_ci		mpi_resize(point->x, ctx->p->nlimbs);
12818c2ecf20Sopenharmony_ci		point->x->nlimbs = ctx->p->nlimbs;
12828c2ecf20Sopenharmony_ci
12838c2ecf20Sopenharmony_ci		q1 = &p1;
12848c2ecf20Sopenharmony_ci		q2 = &p2;
12858c2ecf20Sopenharmony_ci		prd = &p1_;
12868c2ecf20Sopenharmony_ci		sum = &p2_;
12878c2ecf20Sopenharmony_ci
12888c2ecf20Sopenharmony_ci		for (j = nbits-1; j >= 0; j--) {
12898c2ecf20Sopenharmony_ci			MPI_POINT t;
12908c2ecf20Sopenharmony_ci
12918c2ecf20Sopenharmony_ci			sw = mpi_test_bit(scalar, j);
12928c2ecf20Sopenharmony_ci			point_swap_cond(q1, q2, sw, ctx);
12938c2ecf20Sopenharmony_ci			montgomery_ladder(prd, sum, q1, q2, point->x, ctx);
12948c2ecf20Sopenharmony_ci			point_swap_cond(prd, sum, sw, ctx);
12958c2ecf20Sopenharmony_ci			t = q1;  q1 = prd;  prd = t;
12968c2ecf20Sopenharmony_ci			t = q2;  q2 = sum;  sum = t;
12978c2ecf20Sopenharmony_ci		}
12988c2ecf20Sopenharmony_ci
12998c2ecf20Sopenharmony_ci		mpi_clear(result->y);
13008c2ecf20Sopenharmony_ci		sw = (nbits & 1);
13018c2ecf20Sopenharmony_ci		point_swap_cond(&p1, &p1_, sw, ctx);
13028c2ecf20Sopenharmony_ci
13038c2ecf20Sopenharmony_ci		rsize = p1.z->nlimbs;
13048c2ecf20Sopenharmony_ci		MPN_NORMALIZE(p1.z->d, rsize);
13058c2ecf20Sopenharmony_ci		if (rsize == 0) {
13068c2ecf20Sopenharmony_ci			mpi_set_ui(result->x, 1);
13078c2ecf20Sopenharmony_ci			mpi_set_ui(result->z, 0);
13088c2ecf20Sopenharmony_ci		} else {
13098c2ecf20Sopenharmony_ci			z1 = mpi_new(0);
13108c2ecf20Sopenharmony_ci			ec_invm(z1, p1.z, ctx);
13118c2ecf20Sopenharmony_ci			ec_mulm(result->x, p1.x, z1, ctx);
13128c2ecf20Sopenharmony_ci			mpi_set_ui(result->z, 1);
13138c2ecf20Sopenharmony_ci			mpi_free(z1);
13148c2ecf20Sopenharmony_ci		}
13158c2ecf20Sopenharmony_ci
13168c2ecf20Sopenharmony_ci		point_free(&p1);
13178c2ecf20Sopenharmony_ci		point_free(&p2);
13188c2ecf20Sopenharmony_ci		point_free(&p1_);
13198c2ecf20Sopenharmony_ci		point_free(&p2_);
13208c2ecf20Sopenharmony_ci		if (scalar_copied)
13218c2ecf20Sopenharmony_ci			mpi_free(scalar);
13228c2ecf20Sopenharmony_ci		return;
13238c2ecf20Sopenharmony_ci	}
13248c2ecf20Sopenharmony_ci
13258c2ecf20Sopenharmony_ci	x1 = mpi_alloc_like(ctx->p);
13268c2ecf20Sopenharmony_ci	y1 = mpi_alloc_like(ctx->p);
13278c2ecf20Sopenharmony_ci	h  = mpi_alloc_like(ctx->p);
13288c2ecf20Sopenharmony_ci	k  = mpi_copy(scalar);
13298c2ecf20Sopenharmony_ci	yy = mpi_copy(point->y);
13308c2ecf20Sopenharmony_ci
13318c2ecf20Sopenharmony_ci	if (mpi_has_sign(k)) {
13328c2ecf20Sopenharmony_ci		k->sign = 0;
13338c2ecf20Sopenharmony_ci		ec_invm(yy, yy, ctx);
13348c2ecf20Sopenharmony_ci	}
13358c2ecf20Sopenharmony_ci
13368c2ecf20Sopenharmony_ci	if (!mpi_cmp_ui(point->z, 1)) {
13378c2ecf20Sopenharmony_ci		mpi_set(x1, point->x);
13388c2ecf20Sopenharmony_ci		mpi_set(y1, yy);
13398c2ecf20Sopenharmony_ci	} else {
13408c2ecf20Sopenharmony_ci		MPI z2, z3;
13418c2ecf20Sopenharmony_ci
13428c2ecf20Sopenharmony_ci		z2 = mpi_alloc_like(ctx->p);
13438c2ecf20Sopenharmony_ci		z3 = mpi_alloc_like(ctx->p);
13448c2ecf20Sopenharmony_ci		ec_mulm(z2, point->z, point->z, ctx);
13458c2ecf20Sopenharmony_ci		ec_mulm(z3, point->z, z2, ctx);
13468c2ecf20Sopenharmony_ci		ec_invm(z2, z2, ctx);
13478c2ecf20Sopenharmony_ci		ec_mulm(x1, point->x, z2, ctx);
13488c2ecf20Sopenharmony_ci		ec_invm(z3, z3, ctx);
13498c2ecf20Sopenharmony_ci		ec_mulm(y1, yy, z3, ctx);
13508c2ecf20Sopenharmony_ci		mpi_free(z2);
13518c2ecf20Sopenharmony_ci		mpi_free(z3);
13528c2ecf20Sopenharmony_ci	}
13538c2ecf20Sopenharmony_ci	z1 = mpi_copy(mpi_const(MPI_C_ONE));
13548c2ecf20Sopenharmony_ci
13558c2ecf20Sopenharmony_ci	mpi_mul(h, k, mpi_const(MPI_C_THREE)); /* h = 3k */
13568c2ecf20Sopenharmony_ci	loops = mpi_get_nbits(h);
13578c2ecf20Sopenharmony_ci	if (loops < 2) {
13588c2ecf20Sopenharmony_ci		/* If SCALAR is zero, the above mpi_mul sets H to zero and thus
13598c2ecf20Sopenharmony_ci		 * LOOPs will be zero.  To avoid an underflow of I in the main
13608c2ecf20Sopenharmony_ci		 * loop we set LOOP to 2 and the result to (0,0,0).
13618c2ecf20Sopenharmony_ci		 */
13628c2ecf20Sopenharmony_ci		loops = 2;
13638c2ecf20Sopenharmony_ci		mpi_clear(result->x);
13648c2ecf20Sopenharmony_ci		mpi_clear(result->y);
13658c2ecf20Sopenharmony_ci		mpi_clear(result->z);
13668c2ecf20Sopenharmony_ci	} else {
13678c2ecf20Sopenharmony_ci		mpi_set(result->x, point->x);
13688c2ecf20Sopenharmony_ci		mpi_set(result->y, yy);
13698c2ecf20Sopenharmony_ci		mpi_set(result->z, point->z);
13708c2ecf20Sopenharmony_ci	}
13718c2ecf20Sopenharmony_ci	mpi_free(yy); yy = NULL;
13728c2ecf20Sopenharmony_ci
13738c2ecf20Sopenharmony_ci	p1.x = x1; x1 = NULL;
13748c2ecf20Sopenharmony_ci	p1.y = y1; y1 = NULL;
13758c2ecf20Sopenharmony_ci	p1.z = z1; z1 = NULL;
13768c2ecf20Sopenharmony_ci	point_init(&p2);
13778c2ecf20Sopenharmony_ci	point_init(&p1inv);
13788c2ecf20Sopenharmony_ci
13798c2ecf20Sopenharmony_ci	/* Invert point: y = p - y mod p  */
13808c2ecf20Sopenharmony_ci	point_set(&p1inv, &p1);
13818c2ecf20Sopenharmony_ci	ec_subm(p1inv.y, ctx->p, p1inv.y, ctx);
13828c2ecf20Sopenharmony_ci
13838c2ecf20Sopenharmony_ci	for (i = loops-2; i > 0; i--) {
13848c2ecf20Sopenharmony_ci		mpi_ec_dup_point(result, result, ctx);
13858c2ecf20Sopenharmony_ci		if (mpi_test_bit(h, i) == 1 && mpi_test_bit(k, i) == 0) {
13868c2ecf20Sopenharmony_ci			point_set(&p2, result);
13878c2ecf20Sopenharmony_ci			mpi_ec_add_points(result, &p2, &p1, ctx);
13888c2ecf20Sopenharmony_ci		}
13898c2ecf20Sopenharmony_ci		if (mpi_test_bit(h, i) == 0 && mpi_test_bit(k, i) == 1) {
13908c2ecf20Sopenharmony_ci			point_set(&p2, result);
13918c2ecf20Sopenharmony_ci			mpi_ec_add_points(result, &p2, &p1inv, ctx);
13928c2ecf20Sopenharmony_ci		}
13938c2ecf20Sopenharmony_ci	}
13948c2ecf20Sopenharmony_ci
13958c2ecf20Sopenharmony_ci	point_free(&p1);
13968c2ecf20Sopenharmony_ci	point_free(&p2);
13978c2ecf20Sopenharmony_ci	point_free(&p1inv);
13988c2ecf20Sopenharmony_ci	mpi_free(h);
13998c2ecf20Sopenharmony_ci	mpi_free(k);
14008c2ecf20Sopenharmony_ci}
14018c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_mul_point);
14028c2ecf20Sopenharmony_ci
14038c2ecf20Sopenharmony_ci/* Return true if POINT is on the curve described by CTX.  */
14048c2ecf20Sopenharmony_ciint mpi_ec_curve_point(MPI_POINT point, struct mpi_ec_ctx *ctx)
14058c2ecf20Sopenharmony_ci{
14068c2ecf20Sopenharmony_ci	int res = 0;
14078c2ecf20Sopenharmony_ci	MPI x, y, w;
14088c2ecf20Sopenharmony_ci
14098c2ecf20Sopenharmony_ci	x = mpi_new(0);
14108c2ecf20Sopenharmony_ci	y = mpi_new(0);
14118c2ecf20Sopenharmony_ci	w = mpi_new(0);
14128c2ecf20Sopenharmony_ci
14138c2ecf20Sopenharmony_ci	/* Check that the point is in range.  This needs to be done here and
14148c2ecf20Sopenharmony_ci	 * not after conversion to affine coordinates.
14158c2ecf20Sopenharmony_ci	 */
14168c2ecf20Sopenharmony_ci	if (mpi_cmpabs(point->x, ctx->p) >= 0)
14178c2ecf20Sopenharmony_ci		goto leave;
14188c2ecf20Sopenharmony_ci	if (mpi_cmpabs(point->y, ctx->p) >= 0)
14198c2ecf20Sopenharmony_ci		goto leave;
14208c2ecf20Sopenharmony_ci	if (mpi_cmpabs(point->z, ctx->p) >= 0)
14218c2ecf20Sopenharmony_ci		goto leave;
14228c2ecf20Sopenharmony_ci
14238c2ecf20Sopenharmony_ci	switch (ctx->model) {
14248c2ecf20Sopenharmony_ci	case MPI_EC_WEIERSTRASS:
14258c2ecf20Sopenharmony_ci		{
14268c2ecf20Sopenharmony_ci			MPI xxx;
14278c2ecf20Sopenharmony_ci
14288c2ecf20Sopenharmony_ci			if (mpi_ec_get_affine(x, y, point, ctx))
14298c2ecf20Sopenharmony_ci				goto leave;
14308c2ecf20Sopenharmony_ci
14318c2ecf20Sopenharmony_ci			xxx = mpi_new(0);
14328c2ecf20Sopenharmony_ci
14338c2ecf20Sopenharmony_ci			/* y^2 == x^3 + a·x + b */
14348c2ecf20Sopenharmony_ci			ec_pow2(y, y, ctx);
14358c2ecf20Sopenharmony_ci
14368c2ecf20Sopenharmony_ci			ec_pow3(xxx, x, ctx);
14378c2ecf20Sopenharmony_ci			ec_mulm(w, ctx->a, x, ctx);
14388c2ecf20Sopenharmony_ci			ec_addm(w, w, ctx->b, ctx);
14398c2ecf20Sopenharmony_ci			ec_addm(w, w, xxx, ctx);
14408c2ecf20Sopenharmony_ci
14418c2ecf20Sopenharmony_ci			if (!mpi_cmp(y, w))
14428c2ecf20Sopenharmony_ci				res = 1;
14438c2ecf20Sopenharmony_ci
14448c2ecf20Sopenharmony_ci			mpi_free(xxx);
14458c2ecf20Sopenharmony_ci		}
14468c2ecf20Sopenharmony_ci		break;
14478c2ecf20Sopenharmony_ci
14488c2ecf20Sopenharmony_ci	case MPI_EC_MONTGOMERY:
14498c2ecf20Sopenharmony_ci		{
14508c2ecf20Sopenharmony_ci#define xx y
14518c2ecf20Sopenharmony_ci			/* With Montgomery curve, only X-coordinate is valid. */
14528c2ecf20Sopenharmony_ci			if (mpi_ec_get_affine(x, NULL, point, ctx))
14538c2ecf20Sopenharmony_ci				goto leave;
14548c2ecf20Sopenharmony_ci
14558c2ecf20Sopenharmony_ci			/* The equation is: b * y^2 == x^3 + a · x^2 + x */
14568c2ecf20Sopenharmony_ci			/* We check if right hand is quadratic residue or not by
14578c2ecf20Sopenharmony_ci			 * Euler's criterion.
14588c2ecf20Sopenharmony_ci			 */
14598c2ecf20Sopenharmony_ci			/* CTX->A has (a-2)/4 and CTX->B has b^-1 */
14608c2ecf20Sopenharmony_ci			ec_mulm(w, ctx->a, mpi_const(MPI_C_FOUR), ctx);
14618c2ecf20Sopenharmony_ci			ec_addm(w, w, mpi_const(MPI_C_TWO), ctx);
14628c2ecf20Sopenharmony_ci			ec_mulm(w, w, x, ctx);
14638c2ecf20Sopenharmony_ci			ec_pow2(xx, x, ctx);
14648c2ecf20Sopenharmony_ci			ec_addm(w, w, xx, ctx);
14658c2ecf20Sopenharmony_ci			ec_addm(w, w, mpi_const(MPI_C_ONE), ctx);
14668c2ecf20Sopenharmony_ci			ec_mulm(w, w, x, ctx);
14678c2ecf20Sopenharmony_ci			ec_mulm(w, w, ctx->b, ctx);
14688c2ecf20Sopenharmony_ci#undef xx
14698c2ecf20Sopenharmony_ci			/* Compute Euler's criterion: w^(p-1)/2 */
14708c2ecf20Sopenharmony_ci#define p_minus1 y
14718c2ecf20Sopenharmony_ci			ec_subm(p_minus1, ctx->p, mpi_const(MPI_C_ONE), ctx);
14728c2ecf20Sopenharmony_ci			mpi_rshift(p_minus1, p_minus1, 1);
14738c2ecf20Sopenharmony_ci			ec_powm(w, w, p_minus1, ctx);
14748c2ecf20Sopenharmony_ci
14758c2ecf20Sopenharmony_ci			res = !mpi_cmp_ui(w, 1);
14768c2ecf20Sopenharmony_ci#undef p_minus1
14778c2ecf20Sopenharmony_ci		}
14788c2ecf20Sopenharmony_ci		break;
14798c2ecf20Sopenharmony_ci
14808c2ecf20Sopenharmony_ci	case MPI_EC_EDWARDS:
14818c2ecf20Sopenharmony_ci		{
14828c2ecf20Sopenharmony_ci			if (mpi_ec_get_affine(x, y, point, ctx))
14838c2ecf20Sopenharmony_ci				goto leave;
14848c2ecf20Sopenharmony_ci
14858c2ecf20Sopenharmony_ci			mpi_resize(w, ctx->p->nlimbs);
14868c2ecf20Sopenharmony_ci			w->nlimbs = ctx->p->nlimbs;
14878c2ecf20Sopenharmony_ci
14888c2ecf20Sopenharmony_ci			/* a · x^2 + y^2 - 1 - b · x^2 · y^2 == 0 */
14898c2ecf20Sopenharmony_ci			ctx->pow2(x, x, ctx);
14908c2ecf20Sopenharmony_ci			ctx->pow2(y, y, ctx);
14918c2ecf20Sopenharmony_ci			if (ctx->dialect == ECC_DIALECT_ED25519)
14928c2ecf20Sopenharmony_ci				ctx->subm(w, ctx->p, x, ctx);
14938c2ecf20Sopenharmony_ci			else
14948c2ecf20Sopenharmony_ci				ctx->mulm(w, ctx->a, x, ctx);
14958c2ecf20Sopenharmony_ci			ctx->addm(w, w, y, ctx);
14968c2ecf20Sopenharmony_ci			ctx->mulm(x, x, y, ctx);
14978c2ecf20Sopenharmony_ci			ctx->mulm(x, x, ctx->b, ctx);
14988c2ecf20Sopenharmony_ci			ctx->subm(w, w, x, ctx);
14998c2ecf20Sopenharmony_ci			if (!mpi_cmp_ui(w, 1))
15008c2ecf20Sopenharmony_ci				res = 1;
15018c2ecf20Sopenharmony_ci		}
15028c2ecf20Sopenharmony_ci		break;
15038c2ecf20Sopenharmony_ci	}
15048c2ecf20Sopenharmony_ci
15058c2ecf20Sopenharmony_cileave:
15068c2ecf20Sopenharmony_ci	mpi_free(w);
15078c2ecf20Sopenharmony_ci	mpi_free(x);
15088c2ecf20Sopenharmony_ci	mpi_free(y);
15098c2ecf20Sopenharmony_ci
15108c2ecf20Sopenharmony_ci	return res;
15118c2ecf20Sopenharmony_ci}
15128c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_ec_curve_point);
1513