18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/* mpi-pow.c  -  MPI functions
38c2ecf20Sopenharmony_ci *	Copyright (C) 1994, 1996, 1998, 2000 Free Software Foundation, Inc.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * This file is part of GnuPG.
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Note: This code is heavily based on the GNU MP Library.
88c2ecf20Sopenharmony_ci *	 Actually it's the same code with only minor changes in the
98c2ecf20Sopenharmony_ci *	 way the data is stored; this is to support the abstraction
108c2ecf20Sopenharmony_ci *	 of an optional secure memory allocation which may be used
118c2ecf20Sopenharmony_ci *	 to avoid revealing of sensitive data due to paging etc.
128c2ecf20Sopenharmony_ci *	 The GNU MP Library itself is published under the LGPL;
138c2ecf20Sopenharmony_ci *	 however I decided to publish this code under the plain GPL.
148c2ecf20Sopenharmony_ci */
158c2ecf20Sopenharmony_ci
168c2ecf20Sopenharmony_ci#include <linux/sched.h>
178c2ecf20Sopenharmony_ci#include <linux/string.h>
188c2ecf20Sopenharmony_ci#include "mpi-internal.h"
198c2ecf20Sopenharmony_ci#include "longlong.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/****************
228c2ecf20Sopenharmony_ci * RES = BASE ^ EXP mod MOD
238c2ecf20Sopenharmony_ci */
248c2ecf20Sopenharmony_ciint mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
258c2ecf20Sopenharmony_ci{
268c2ecf20Sopenharmony_ci	mpi_ptr_t mp_marker = NULL, bp_marker = NULL, ep_marker = NULL;
278c2ecf20Sopenharmony_ci	struct karatsuba_ctx karactx = {};
288c2ecf20Sopenharmony_ci	mpi_ptr_t xp_marker = NULL;
298c2ecf20Sopenharmony_ci	mpi_ptr_t tspace = NULL;
308c2ecf20Sopenharmony_ci	mpi_ptr_t rp, ep, mp, bp;
318c2ecf20Sopenharmony_ci	mpi_size_t esize, msize, bsize, rsize;
328c2ecf20Sopenharmony_ci	int msign, bsign, rsign;
338c2ecf20Sopenharmony_ci	mpi_size_t size;
348c2ecf20Sopenharmony_ci	int mod_shift_cnt;
358c2ecf20Sopenharmony_ci	int negative_result;
368c2ecf20Sopenharmony_ci	int assign_rp = 0;
378c2ecf20Sopenharmony_ci	mpi_size_t tsize = 0;	/* to avoid compiler warning */
388c2ecf20Sopenharmony_ci	/* fixme: we should check that the warning is void */
398c2ecf20Sopenharmony_ci	int rc = -ENOMEM;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_ci	esize = exp->nlimbs;
428c2ecf20Sopenharmony_ci	msize = mod->nlimbs;
438c2ecf20Sopenharmony_ci	size = 2 * msize;
448c2ecf20Sopenharmony_ci	msign = mod->sign;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	rp = res->d;
478c2ecf20Sopenharmony_ci	ep = exp->d;
488c2ecf20Sopenharmony_ci
498c2ecf20Sopenharmony_ci	if (!msize)
508c2ecf20Sopenharmony_ci		return -EINVAL;
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	if (!esize) {
538c2ecf20Sopenharmony_ci		/* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0
548c2ecf20Sopenharmony_ci		 * depending on if MOD equals 1.  */
558c2ecf20Sopenharmony_ci		res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
568c2ecf20Sopenharmony_ci		if (res->nlimbs) {
578c2ecf20Sopenharmony_ci			if (mpi_resize(res, 1) < 0)
588c2ecf20Sopenharmony_ci				goto enomem;
598c2ecf20Sopenharmony_ci			rp = res->d;
608c2ecf20Sopenharmony_ci			rp[0] = 1;
618c2ecf20Sopenharmony_ci		}
628c2ecf20Sopenharmony_ci		res->sign = 0;
638c2ecf20Sopenharmony_ci		goto leave;
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	/* Normalize MOD (i.e. make its most significant bit set) as required by
678c2ecf20Sopenharmony_ci	 * mpn_divrem.  This will make the intermediate values in the calculation
688c2ecf20Sopenharmony_ci	 * slightly larger, but the correct result is obtained after a final
698c2ecf20Sopenharmony_ci	 * reduction using the original MOD value.  */
708c2ecf20Sopenharmony_ci	mp = mp_marker = mpi_alloc_limb_space(msize);
718c2ecf20Sopenharmony_ci	if (!mp)
728c2ecf20Sopenharmony_ci		goto enomem;
738c2ecf20Sopenharmony_ci	mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]);
748c2ecf20Sopenharmony_ci	if (mod_shift_cnt)
758c2ecf20Sopenharmony_ci		mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt);
768c2ecf20Sopenharmony_ci	else
778c2ecf20Sopenharmony_ci		MPN_COPY(mp, mod->d, msize);
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	bsize = base->nlimbs;
808c2ecf20Sopenharmony_ci	bsign = base->sign;
818c2ecf20Sopenharmony_ci	if (bsize > msize) {	/* The base is larger than the module. Reduce it. */
828c2ecf20Sopenharmony_ci		/* Allocate (BSIZE + 1) with space for remainder and quotient.
838c2ecf20Sopenharmony_ci		 * (The quotient is (bsize - msize + 1) limbs.)  */
848c2ecf20Sopenharmony_ci		bp = bp_marker = mpi_alloc_limb_space(bsize + 1);
858c2ecf20Sopenharmony_ci		if (!bp)
868c2ecf20Sopenharmony_ci			goto enomem;
878c2ecf20Sopenharmony_ci		MPN_COPY(bp, base->d, bsize);
888c2ecf20Sopenharmony_ci		/* We don't care about the quotient, store it above the remainder,
898c2ecf20Sopenharmony_ci		 * at BP + MSIZE.  */
908c2ecf20Sopenharmony_ci		mpihelp_divrem(bp + msize, 0, bp, bsize, mp, msize);
918c2ecf20Sopenharmony_ci		bsize = msize;
928c2ecf20Sopenharmony_ci		/* Canonicalize the base, since we are going to multiply with it
938c2ecf20Sopenharmony_ci		 * quite a few times.  */
948c2ecf20Sopenharmony_ci		MPN_NORMALIZE(bp, bsize);
958c2ecf20Sopenharmony_ci	} else
968c2ecf20Sopenharmony_ci		bp = base->d;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	if (!bsize) {
998c2ecf20Sopenharmony_ci		res->nlimbs = 0;
1008c2ecf20Sopenharmony_ci		res->sign = 0;
1018c2ecf20Sopenharmony_ci		goto leave;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	if (res->alloced < size) {
1058c2ecf20Sopenharmony_ci		/* We have to allocate more space for RES.  If any of the input
1068c2ecf20Sopenharmony_ci		 * parameters are identical to RES, defer deallocation of the old
1078c2ecf20Sopenharmony_ci		 * space.  */
1088c2ecf20Sopenharmony_ci		if (rp == ep || rp == mp || rp == bp) {
1098c2ecf20Sopenharmony_ci			rp = mpi_alloc_limb_space(size);
1108c2ecf20Sopenharmony_ci			if (!rp)
1118c2ecf20Sopenharmony_ci				goto enomem;
1128c2ecf20Sopenharmony_ci			assign_rp = 1;
1138c2ecf20Sopenharmony_ci		} else {
1148c2ecf20Sopenharmony_ci			if (mpi_resize(res, size) < 0)
1158c2ecf20Sopenharmony_ci				goto enomem;
1168c2ecf20Sopenharmony_ci			rp = res->d;
1178c2ecf20Sopenharmony_ci		}
1188c2ecf20Sopenharmony_ci	} else {		/* Make BASE, EXP and MOD not overlap with RES.  */
1198c2ecf20Sopenharmony_ci		if (rp == bp) {
1208c2ecf20Sopenharmony_ci			/* RES and BASE are identical.  Allocate temp. space for BASE.  */
1218c2ecf20Sopenharmony_ci			BUG_ON(bp_marker);
1228c2ecf20Sopenharmony_ci			bp = bp_marker = mpi_alloc_limb_space(bsize);
1238c2ecf20Sopenharmony_ci			if (!bp)
1248c2ecf20Sopenharmony_ci				goto enomem;
1258c2ecf20Sopenharmony_ci			MPN_COPY(bp, rp, bsize);
1268c2ecf20Sopenharmony_ci		}
1278c2ecf20Sopenharmony_ci		if (rp == ep) {
1288c2ecf20Sopenharmony_ci			/* RES and EXP are identical.  Allocate temp. space for EXP.  */
1298c2ecf20Sopenharmony_ci			ep = ep_marker = mpi_alloc_limb_space(esize);
1308c2ecf20Sopenharmony_ci			if (!ep)
1318c2ecf20Sopenharmony_ci				goto enomem;
1328c2ecf20Sopenharmony_ci			MPN_COPY(ep, rp, esize);
1338c2ecf20Sopenharmony_ci		}
1348c2ecf20Sopenharmony_ci		if (rp == mp) {
1358c2ecf20Sopenharmony_ci			/* RES and MOD are identical.  Allocate temporary space for MOD. */
1368c2ecf20Sopenharmony_ci			BUG_ON(mp_marker);
1378c2ecf20Sopenharmony_ci			mp = mp_marker = mpi_alloc_limb_space(msize);
1388c2ecf20Sopenharmony_ci			if (!mp)
1398c2ecf20Sopenharmony_ci				goto enomem;
1408c2ecf20Sopenharmony_ci			MPN_COPY(mp, rp, msize);
1418c2ecf20Sopenharmony_ci		}
1428c2ecf20Sopenharmony_ci	}
1438c2ecf20Sopenharmony_ci
1448c2ecf20Sopenharmony_ci	MPN_COPY(rp, bp, bsize);
1458c2ecf20Sopenharmony_ci	rsize = bsize;
1468c2ecf20Sopenharmony_ci	rsign = bsign;
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	{
1498c2ecf20Sopenharmony_ci		mpi_size_t i;
1508c2ecf20Sopenharmony_ci		mpi_ptr_t xp;
1518c2ecf20Sopenharmony_ci		int c;
1528c2ecf20Sopenharmony_ci		mpi_limb_t e;
1538c2ecf20Sopenharmony_ci		mpi_limb_t carry_limb;
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci		xp = xp_marker = mpi_alloc_limb_space(2 * (msize + 1));
1568c2ecf20Sopenharmony_ci		if (!xp)
1578c2ecf20Sopenharmony_ci			goto enomem;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci		negative_result = (ep[0] & 1) && base->sign;
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci		i = esize - 1;
1628c2ecf20Sopenharmony_ci		e = ep[i];
1638c2ecf20Sopenharmony_ci		c = count_leading_zeros(e);
1648c2ecf20Sopenharmony_ci		e = (e << c) << 1;	/* shift the exp bits to the left, lose msb */
1658c2ecf20Sopenharmony_ci		c = BITS_PER_MPI_LIMB - 1 - c;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci		/* Main loop.
1688c2ecf20Sopenharmony_ci		 *
1698c2ecf20Sopenharmony_ci		 * Make the result be pointed to alternately by XP and RP.  This
1708c2ecf20Sopenharmony_ci		 * helps us avoid block copying, which would otherwise be necessary
1718c2ecf20Sopenharmony_ci		 * with the overlap restrictions of mpihelp_divmod. With 50% probability
1728c2ecf20Sopenharmony_ci		 * the result after this loop will be in the area originally pointed
1738c2ecf20Sopenharmony_ci		 * by RP (==RES->d), and with 50% probability in the area originally
1748c2ecf20Sopenharmony_ci		 * pointed to by XP.
1758c2ecf20Sopenharmony_ci		 */
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		for (;;) {
1788c2ecf20Sopenharmony_ci			while (c) {
1798c2ecf20Sopenharmony_ci				mpi_ptr_t tp;
1808c2ecf20Sopenharmony_ci				mpi_size_t xsize;
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci				/*if (mpihelp_mul_n(xp, rp, rp, rsize) < 0) goto enomem */
1838c2ecf20Sopenharmony_ci				if (rsize < KARATSUBA_THRESHOLD)
1848c2ecf20Sopenharmony_ci					mpih_sqr_n_basecase(xp, rp, rsize);
1858c2ecf20Sopenharmony_ci				else {
1868c2ecf20Sopenharmony_ci					if (!tspace) {
1878c2ecf20Sopenharmony_ci						tsize = 2 * rsize;
1888c2ecf20Sopenharmony_ci						tspace =
1898c2ecf20Sopenharmony_ci						    mpi_alloc_limb_space(tsize);
1908c2ecf20Sopenharmony_ci						if (!tspace)
1918c2ecf20Sopenharmony_ci							goto enomem;
1928c2ecf20Sopenharmony_ci					} else if (tsize < (2 * rsize)) {
1938c2ecf20Sopenharmony_ci						mpi_free_limb_space(tspace);
1948c2ecf20Sopenharmony_ci						tsize = 2 * rsize;
1958c2ecf20Sopenharmony_ci						tspace =
1968c2ecf20Sopenharmony_ci						    mpi_alloc_limb_space(tsize);
1978c2ecf20Sopenharmony_ci						if (!tspace)
1988c2ecf20Sopenharmony_ci							goto enomem;
1998c2ecf20Sopenharmony_ci					}
2008c2ecf20Sopenharmony_ci					mpih_sqr_n(xp, rp, rsize, tspace);
2018c2ecf20Sopenharmony_ci				}
2028c2ecf20Sopenharmony_ci
2038c2ecf20Sopenharmony_ci				xsize = 2 * rsize;
2048c2ecf20Sopenharmony_ci				if (xsize > msize) {
2058c2ecf20Sopenharmony_ci					mpihelp_divrem(xp + msize, 0, xp, xsize,
2068c2ecf20Sopenharmony_ci						       mp, msize);
2078c2ecf20Sopenharmony_ci					xsize = msize;
2088c2ecf20Sopenharmony_ci				}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci				tp = rp;
2118c2ecf20Sopenharmony_ci				rp = xp;
2128c2ecf20Sopenharmony_ci				xp = tp;
2138c2ecf20Sopenharmony_ci				rsize = xsize;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci				if ((mpi_limb_signed_t) e < 0) {
2168c2ecf20Sopenharmony_ci					/*mpihelp_mul( xp, rp, rsize, bp, bsize ); */
2178c2ecf20Sopenharmony_ci					if (bsize < KARATSUBA_THRESHOLD) {
2188c2ecf20Sopenharmony_ci						mpi_limb_t tmp;
2198c2ecf20Sopenharmony_ci						if (mpihelp_mul
2208c2ecf20Sopenharmony_ci						    (xp, rp, rsize, bp, bsize,
2218c2ecf20Sopenharmony_ci						     &tmp) < 0)
2228c2ecf20Sopenharmony_ci							goto enomem;
2238c2ecf20Sopenharmony_ci					} else {
2248c2ecf20Sopenharmony_ci						if (mpihelp_mul_karatsuba_case
2258c2ecf20Sopenharmony_ci						    (xp, rp, rsize, bp, bsize,
2268c2ecf20Sopenharmony_ci						     &karactx) < 0)
2278c2ecf20Sopenharmony_ci							goto enomem;
2288c2ecf20Sopenharmony_ci					}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci					xsize = rsize + bsize;
2318c2ecf20Sopenharmony_ci					if (xsize > msize) {
2328c2ecf20Sopenharmony_ci						mpihelp_divrem(xp + msize, 0,
2338c2ecf20Sopenharmony_ci							       xp, xsize, mp,
2348c2ecf20Sopenharmony_ci							       msize);
2358c2ecf20Sopenharmony_ci						xsize = msize;
2368c2ecf20Sopenharmony_ci					}
2378c2ecf20Sopenharmony_ci
2388c2ecf20Sopenharmony_ci					tp = rp;
2398c2ecf20Sopenharmony_ci					rp = xp;
2408c2ecf20Sopenharmony_ci					xp = tp;
2418c2ecf20Sopenharmony_ci					rsize = xsize;
2428c2ecf20Sopenharmony_ci				}
2438c2ecf20Sopenharmony_ci				e <<= 1;
2448c2ecf20Sopenharmony_ci				c--;
2458c2ecf20Sopenharmony_ci				cond_resched();
2468c2ecf20Sopenharmony_ci			}
2478c2ecf20Sopenharmony_ci
2488c2ecf20Sopenharmony_ci			i--;
2498c2ecf20Sopenharmony_ci			if (i < 0)
2508c2ecf20Sopenharmony_ci				break;
2518c2ecf20Sopenharmony_ci			e = ep[i];
2528c2ecf20Sopenharmony_ci			c = BITS_PER_MPI_LIMB;
2538c2ecf20Sopenharmony_ci		}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_ci		/* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT
2568c2ecf20Sopenharmony_ci		 * steps.  Adjust the result by reducing it with the original MOD.
2578c2ecf20Sopenharmony_ci		 *
2588c2ecf20Sopenharmony_ci		 * Also make sure the result is put in RES->d (where it already
2598c2ecf20Sopenharmony_ci		 * might be, see above).
2608c2ecf20Sopenharmony_ci		 */
2618c2ecf20Sopenharmony_ci		if (mod_shift_cnt) {
2628c2ecf20Sopenharmony_ci			carry_limb =
2638c2ecf20Sopenharmony_ci			    mpihelp_lshift(res->d, rp, rsize, mod_shift_cnt);
2648c2ecf20Sopenharmony_ci			rp = res->d;
2658c2ecf20Sopenharmony_ci			if (carry_limb) {
2668c2ecf20Sopenharmony_ci				rp[rsize] = carry_limb;
2678c2ecf20Sopenharmony_ci				rsize++;
2688c2ecf20Sopenharmony_ci			}
2698c2ecf20Sopenharmony_ci		} else {
2708c2ecf20Sopenharmony_ci			MPN_COPY(res->d, rp, rsize);
2718c2ecf20Sopenharmony_ci			rp = res->d;
2728c2ecf20Sopenharmony_ci		}
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci		if (rsize >= msize) {
2758c2ecf20Sopenharmony_ci			mpihelp_divrem(rp + msize, 0, rp, rsize, mp, msize);
2768c2ecf20Sopenharmony_ci			rsize = msize;
2778c2ecf20Sopenharmony_ci		}
2788c2ecf20Sopenharmony_ci
2798c2ecf20Sopenharmony_ci		/* Remove any leading zero words from the result.  */
2808c2ecf20Sopenharmony_ci		if (mod_shift_cnt)
2818c2ecf20Sopenharmony_ci			mpihelp_rshift(rp, rp, rsize, mod_shift_cnt);
2828c2ecf20Sopenharmony_ci		MPN_NORMALIZE(rp, rsize);
2838c2ecf20Sopenharmony_ci	}
2848c2ecf20Sopenharmony_ci
2858c2ecf20Sopenharmony_ci	if (negative_result && rsize) {
2868c2ecf20Sopenharmony_ci		if (mod_shift_cnt)
2878c2ecf20Sopenharmony_ci			mpihelp_rshift(mp, mp, msize, mod_shift_cnt);
2888c2ecf20Sopenharmony_ci		mpihelp_sub(rp, mp, msize, rp, rsize);
2898c2ecf20Sopenharmony_ci		rsize = msize;
2908c2ecf20Sopenharmony_ci		rsign = msign;
2918c2ecf20Sopenharmony_ci		MPN_NORMALIZE(rp, rsize);
2928c2ecf20Sopenharmony_ci	}
2938c2ecf20Sopenharmony_ci	res->nlimbs = rsize;
2948c2ecf20Sopenharmony_ci	res->sign = rsign;
2958c2ecf20Sopenharmony_ci
2968c2ecf20Sopenharmony_cileave:
2978c2ecf20Sopenharmony_ci	rc = 0;
2988c2ecf20Sopenharmony_cienomem:
2998c2ecf20Sopenharmony_ci	mpihelp_release_karatsuba_ctx(&karactx);
3008c2ecf20Sopenharmony_ci	if (assign_rp)
3018c2ecf20Sopenharmony_ci		mpi_assign_limb_space(res, rp, size);
3028c2ecf20Sopenharmony_ci	if (mp_marker)
3038c2ecf20Sopenharmony_ci		mpi_free_limb_space(mp_marker);
3048c2ecf20Sopenharmony_ci	if (bp_marker)
3058c2ecf20Sopenharmony_ci		mpi_free_limb_space(bp_marker);
3068c2ecf20Sopenharmony_ci	if (ep_marker)
3078c2ecf20Sopenharmony_ci		mpi_free_limb_space(ep_marker);
3088c2ecf20Sopenharmony_ci	if (xp_marker)
3098c2ecf20Sopenharmony_ci		mpi_free_limb_space(xp_marker);
3108c2ecf20Sopenharmony_ci	if (tspace)
3118c2ecf20Sopenharmony_ci		mpi_free_limb_space(tspace);
3128c2ecf20Sopenharmony_ci	return rc;
3138c2ecf20Sopenharmony_ci}
3148c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mpi_powm);
315