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