1*2a598d0bSHerbert Xu // SPDX-License-Identifier: GPL-2.0-or-later 2*2a598d0bSHerbert Xu /* mpi-pow.c - MPI functions 3*2a598d0bSHerbert Xu * Copyright (C) 1994, 1996, 1998, 2000 Free Software Foundation, Inc. 4*2a598d0bSHerbert Xu * 5*2a598d0bSHerbert Xu * This file is part of GnuPG. 6*2a598d0bSHerbert Xu * 7*2a598d0bSHerbert Xu * Note: This code is heavily based on the GNU MP Library. 8*2a598d0bSHerbert Xu * Actually it's the same code with only minor changes in the 9*2a598d0bSHerbert Xu * way the data is stored; this is to support the abstraction 10*2a598d0bSHerbert Xu * of an optional secure memory allocation which may be used 11*2a598d0bSHerbert Xu * to avoid revealing of sensitive data due to paging etc. 12*2a598d0bSHerbert Xu * The GNU MP Library itself is published under the LGPL; 13*2a598d0bSHerbert Xu * however I decided to publish this code under the plain GPL. 14*2a598d0bSHerbert Xu */ 15*2a598d0bSHerbert Xu 16*2a598d0bSHerbert Xu #include <linux/sched.h> 17*2a598d0bSHerbert Xu #include <linux/string.h> 18*2a598d0bSHerbert Xu #include "mpi-internal.h" 19*2a598d0bSHerbert Xu #include "longlong.h" 20*2a598d0bSHerbert Xu 21*2a598d0bSHerbert Xu /**************** 22*2a598d0bSHerbert Xu * RES = BASE ^ EXP mod MOD 23*2a598d0bSHerbert Xu */ 24*2a598d0bSHerbert Xu int mpi_powm(MPI res, MPI base, MPI exp, MPI mod) 25*2a598d0bSHerbert Xu { 26*2a598d0bSHerbert Xu mpi_ptr_t mp_marker = NULL, bp_marker = NULL, ep_marker = NULL; 27*2a598d0bSHerbert Xu struct karatsuba_ctx karactx = {}; 28*2a598d0bSHerbert Xu mpi_ptr_t xp_marker = NULL; 29*2a598d0bSHerbert Xu mpi_ptr_t tspace = NULL; 30*2a598d0bSHerbert Xu mpi_ptr_t rp, ep, mp, bp; 31*2a598d0bSHerbert Xu mpi_size_t esize, msize, bsize, rsize; 32*2a598d0bSHerbert Xu int msign, bsign, rsign; 33*2a598d0bSHerbert Xu mpi_size_t size; 34*2a598d0bSHerbert Xu int mod_shift_cnt; 35*2a598d0bSHerbert Xu int negative_result; 36*2a598d0bSHerbert Xu int assign_rp = 0; 37*2a598d0bSHerbert Xu mpi_size_t tsize = 0; /* to avoid compiler warning */ 38*2a598d0bSHerbert Xu /* fixme: we should check that the warning is void */ 39*2a598d0bSHerbert Xu int rc = -ENOMEM; 40*2a598d0bSHerbert Xu 41*2a598d0bSHerbert Xu esize = exp->nlimbs; 42*2a598d0bSHerbert Xu msize = mod->nlimbs; 43*2a598d0bSHerbert Xu size = 2 * msize; 44*2a598d0bSHerbert Xu msign = mod->sign; 45*2a598d0bSHerbert Xu 46*2a598d0bSHerbert Xu rp = res->d; 47*2a598d0bSHerbert Xu ep = exp->d; 48*2a598d0bSHerbert Xu 49*2a598d0bSHerbert Xu if (!msize) 50*2a598d0bSHerbert Xu return -EINVAL; 51*2a598d0bSHerbert Xu 52*2a598d0bSHerbert Xu if (!esize) { 53*2a598d0bSHerbert Xu /* Exponent is zero, result is 1 mod MOD, i.e., 1 or 0 54*2a598d0bSHerbert Xu * depending on if MOD equals 1. */ 55*2a598d0bSHerbert Xu res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1; 56*2a598d0bSHerbert Xu if (res->nlimbs) { 57*2a598d0bSHerbert Xu if (mpi_resize(res, 1) < 0) 58*2a598d0bSHerbert Xu goto enomem; 59*2a598d0bSHerbert Xu rp = res->d; 60*2a598d0bSHerbert Xu rp[0] = 1; 61*2a598d0bSHerbert Xu } 62*2a598d0bSHerbert Xu res->sign = 0; 63*2a598d0bSHerbert Xu goto leave; 64*2a598d0bSHerbert Xu } 65*2a598d0bSHerbert Xu 66*2a598d0bSHerbert Xu /* Normalize MOD (i.e. make its most significant bit set) as required by 67*2a598d0bSHerbert Xu * mpn_divrem. This will make the intermediate values in the calculation 68*2a598d0bSHerbert Xu * slightly larger, but the correct result is obtained after a final 69*2a598d0bSHerbert Xu * reduction using the original MOD value. */ 70*2a598d0bSHerbert Xu mp = mp_marker = mpi_alloc_limb_space(msize); 71*2a598d0bSHerbert Xu if (!mp) 72*2a598d0bSHerbert Xu goto enomem; 73*2a598d0bSHerbert Xu mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]); 74*2a598d0bSHerbert Xu if (mod_shift_cnt) 75*2a598d0bSHerbert Xu mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt); 76*2a598d0bSHerbert Xu else 77*2a598d0bSHerbert Xu MPN_COPY(mp, mod->d, msize); 78*2a598d0bSHerbert Xu 79*2a598d0bSHerbert Xu bsize = base->nlimbs; 80*2a598d0bSHerbert Xu bsign = base->sign; 81*2a598d0bSHerbert Xu if (bsize > msize) { /* The base is larger than the module. Reduce it. */ 82*2a598d0bSHerbert Xu /* Allocate (BSIZE + 1) with space for remainder and quotient. 83*2a598d0bSHerbert Xu * (The quotient is (bsize - msize + 1) limbs.) */ 84*2a598d0bSHerbert Xu bp = bp_marker = mpi_alloc_limb_space(bsize + 1); 85*2a598d0bSHerbert Xu if (!bp) 86*2a598d0bSHerbert Xu goto enomem; 87*2a598d0bSHerbert Xu MPN_COPY(bp, base->d, bsize); 88*2a598d0bSHerbert Xu /* We don't care about the quotient, store it above the remainder, 89*2a598d0bSHerbert Xu * at BP + MSIZE. */ 90*2a598d0bSHerbert Xu mpihelp_divrem(bp + msize, 0, bp, bsize, mp, msize); 91*2a598d0bSHerbert Xu bsize = msize; 92*2a598d0bSHerbert Xu /* Canonicalize the base, since we are going to multiply with it 93*2a598d0bSHerbert Xu * quite a few times. */ 94*2a598d0bSHerbert Xu MPN_NORMALIZE(bp, bsize); 95*2a598d0bSHerbert Xu } else 96*2a598d0bSHerbert Xu bp = base->d; 97*2a598d0bSHerbert Xu 98*2a598d0bSHerbert Xu if (!bsize) { 99*2a598d0bSHerbert Xu res->nlimbs = 0; 100*2a598d0bSHerbert Xu res->sign = 0; 101*2a598d0bSHerbert Xu goto leave; 102*2a598d0bSHerbert Xu } 103*2a598d0bSHerbert Xu 104*2a598d0bSHerbert Xu if (res->alloced < size) { 105*2a598d0bSHerbert Xu /* We have to allocate more space for RES. If any of the input 106*2a598d0bSHerbert Xu * parameters are identical to RES, defer deallocation of the old 107*2a598d0bSHerbert Xu * space. */ 108*2a598d0bSHerbert Xu if (rp == ep || rp == mp || rp == bp) { 109*2a598d0bSHerbert Xu rp = mpi_alloc_limb_space(size); 110*2a598d0bSHerbert Xu if (!rp) 111*2a598d0bSHerbert Xu goto enomem; 112*2a598d0bSHerbert Xu assign_rp = 1; 113*2a598d0bSHerbert Xu } else { 114*2a598d0bSHerbert Xu if (mpi_resize(res, size) < 0) 115*2a598d0bSHerbert Xu goto enomem; 116*2a598d0bSHerbert Xu rp = res->d; 117*2a598d0bSHerbert Xu } 118*2a598d0bSHerbert Xu } else { /* Make BASE, EXP and MOD not overlap with RES. */ 119*2a598d0bSHerbert Xu if (rp == bp) { 120*2a598d0bSHerbert Xu /* RES and BASE are identical. Allocate temp. space for BASE. */ 121*2a598d0bSHerbert Xu BUG_ON(bp_marker); 122*2a598d0bSHerbert Xu bp = bp_marker = mpi_alloc_limb_space(bsize); 123*2a598d0bSHerbert Xu if (!bp) 124*2a598d0bSHerbert Xu goto enomem; 125*2a598d0bSHerbert Xu MPN_COPY(bp, rp, bsize); 126*2a598d0bSHerbert Xu } 127*2a598d0bSHerbert Xu if (rp == ep) { 128*2a598d0bSHerbert Xu /* RES and EXP are identical. Allocate temp. space for EXP. */ 129*2a598d0bSHerbert Xu ep = ep_marker = mpi_alloc_limb_space(esize); 130*2a598d0bSHerbert Xu if (!ep) 131*2a598d0bSHerbert Xu goto enomem; 132*2a598d0bSHerbert Xu MPN_COPY(ep, rp, esize); 133*2a598d0bSHerbert Xu } 134*2a598d0bSHerbert Xu if (rp == mp) { 135*2a598d0bSHerbert Xu /* RES and MOD are identical. Allocate temporary space for MOD. */ 136*2a598d0bSHerbert Xu BUG_ON(mp_marker); 137*2a598d0bSHerbert Xu mp = mp_marker = mpi_alloc_limb_space(msize); 138*2a598d0bSHerbert Xu if (!mp) 139*2a598d0bSHerbert Xu goto enomem; 140*2a598d0bSHerbert Xu MPN_COPY(mp, rp, msize); 141*2a598d0bSHerbert Xu } 142*2a598d0bSHerbert Xu } 143*2a598d0bSHerbert Xu 144*2a598d0bSHerbert Xu MPN_COPY(rp, bp, bsize); 145*2a598d0bSHerbert Xu rsize = bsize; 146*2a598d0bSHerbert Xu rsign = bsign; 147*2a598d0bSHerbert Xu 148*2a598d0bSHerbert Xu { 149*2a598d0bSHerbert Xu mpi_size_t i; 150*2a598d0bSHerbert Xu mpi_ptr_t xp; 151*2a598d0bSHerbert Xu int c; 152*2a598d0bSHerbert Xu mpi_limb_t e; 153*2a598d0bSHerbert Xu mpi_limb_t carry_limb; 154*2a598d0bSHerbert Xu 155*2a598d0bSHerbert Xu xp = xp_marker = mpi_alloc_limb_space(2 * (msize + 1)); 156*2a598d0bSHerbert Xu if (!xp) 157*2a598d0bSHerbert Xu goto enomem; 158*2a598d0bSHerbert Xu 159*2a598d0bSHerbert Xu negative_result = (ep[0] & 1) && base->sign; 160*2a598d0bSHerbert Xu 161*2a598d0bSHerbert Xu i = esize - 1; 162*2a598d0bSHerbert Xu e = ep[i]; 163*2a598d0bSHerbert Xu c = count_leading_zeros(e); 164*2a598d0bSHerbert Xu e = (e << c) << 1; /* shift the exp bits to the left, lose msb */ 165*2a598d0bSHerbert Xu c = BITS_PER_MPI_LIMB - 1 - c; 166*2a598d0bSHerbert Xu 167*2a598d0bSHerbert Xu /* Main loop. 168*2a598d0bSHerbert Xu * 169*2a598d0bSHerbert Xu * Make the result be pointed to alternately by XP and RP. This 170*2a598d0bSHerbert Xu * helps us avoid block copying, which would otherwise be necessary 171*2a598d0bSHerbert Xu * with the overlap restrictions of mpihelp_divmod. With 50% probability 172*2a598d0bSHerbert Xu * the result after this loop will be in the area originally pointed 173*2a598d0bSHerbert Xu * by RP (==RES->d), and with 50% probability in the area originally 174*2a598d0bSHerbert Xu * pointed to by XP. 175*2a598d0bSHerbert Xu */ 176*2a598d0bSHerbert Xu 177*2a598d0bSHerbert Xu for (;;) { 178*2a598d0bSHerbert Xu while (c) { 179*2a598d0bSHerbert Xu mpi_ptr_t tp; 180*2a598d0bSHerbert Xu mpi_size_t xsize; 181*2a598d0bSHerbert Xu 182*2a598d0bSHerbert Xu /*if (mpihelp_mul_n(xp, rp, rp, rsize) < 0) goto enomem */ 183*2a598d0bSHerbert Xu if (rsize < KARATSUBA_THRESHOLD) 184*2a598d0bSHerbert Xu mpih_sqr_n_basecase(xp, rp, rsize); 185*2a598d0bSHerbert Xu else { 186*2a598d0bSHerbert Xu if (!tspace) { 187*2a598d0bSHerbert Xu tsize = 2 * rsize; 188*2a598d0bSHerbert Xu tspace = 189*2a598d0bSHerbert Xu mpi_alloc_limb_space(tsize); 190*2a598d0bSHerbert Xu if (!tspace) 191*2a598d0bSHerbert Xu goto enomem; 192*2a598d0bSHerbert Xu } else if (tsize < (2 * rsize)) { 193*2a598d0bSHerbert Xu mpi_free_limb_space(tspace); 194*2a598d0bSHerbert Xu tsize = 2 * rsize; 195*2a598d0bSHerbert Xu tspace = 196*2a598d0bSHerbert Xu mpi_alloc_limb_space(tsize); 197*2a598d0bSHerbert Xu if (!tspace) 198*2a598d0bSHerbert Xu goto enomem; 199*2a598d0bSHerbert Xu } 200*2a598d0bSHerbert Xu mpih_sqr_n(xp, rp, rsize, tspace); 201*2a598d0bSHerbert Xu } 202*2a598d0bSHerbert Xu 203*2a598d0bSHerbert Xu xsize = 2 * rsize; 204*2a598d0bSHerbert Xu if (xsize > msize) { 205*2a598d0bSHerbert Xu mpihelp_divrem(xp + msize, 0, xp, xsize, 206*2a598d0bSHerbert Xu mp, msize); 207*2a598d0bSHerbert Xu xsize = msize; 208*2a598d0bSHerbert Xu } 209*2a598d0bSHerbert Xu 210*2a598d0bSHerbert Xu tp = rp; 211*2a598d0bSHerbert Xu rp = xp; 212*2a598d0bSHerbert Xu xp = tp; 213*2a598d0bSHerbert Xu rsize = xsize; 214*2a598d0bSHerbert Xu 215*2a598d0bSHerbert Xu if ((mpi_limb_signed_t) e < 0) { 216*2a598d0bSHerbert Xu /*mpihelp_mul( xp, rp, rsize, bp, bsize ); */ 217*2a598d0bSHerbert Xu if (bsize < KARATSUBA_THRESHOLD) { 218*2a598d0bSHerbert Xu mpi_limb_t tmp; 219*2a598d0bSHerbert Xu if (mpihelp_mul 220*2a598d0bSHerbert Xu (xp, rp, rsize, bp, bsize, 221*2a598d0bSHerbert Xu &tmp) < 0) 222*2a598d0bSHerbert Xu goto enomem; 223*2a598d0bSHerbert Xu } else { 224*2a598d0bSHerbert Xu if (mpihelp_mul_karatsuba_case 225*2a598d0bSHerbert Xu (xp, rp, rsize, bp, bsize, 226*2a598d0bSHerbert Xu &karactx) < 0) 227*2a598d0bSHerbert Xu goto enomem; 228*2a598d0bSHerbert Xu } 229*2a598d0bSHerbert Xu 230*2a598d0bSHerbert Xu xsize = rsize + bsize; 231*2a598d0bSHerbert Xu if (xsize > msize) { 232*2a598d0bSHerbert Xu mpihelp_divrem(xp + msize, 0, 233*2a598d0bSHerbert Xu xp, xsize, mp, 234*2a598d0bSHerbert Xu msize); 235*2a598d0bSHerbert Xu xsize = msize; 236*2a598d0bSHerbert Xu } 237*2a598d0bSHerbert Xu 238*2a598d0bSHerbert Xu tp = rp; 239*2a598d0bSHerbert Xu rp = xp; 240*2a598d0bSHerbert Xu xp = tp; 241*2a598d0bSHerbert Xu rsize = xsize; 242*2a598d0bSHerbert Xu } 243*2a598d0bSHerbert Xu e <<= 1; 244*2a598d0bSHerbert Xu c--; 245*2a598d0bSHerbert Xu cond_resched(); 246*2a598d0bSHerbert Xu } 247*2a598d0bSHerbert Xu 248*2a598d0bSHerbert Xu i--; 249*2a598d0bSHerbert Xu if (i < 0) 250*2a598d0bSHerbert Xu break; 251*2a598d0bSHerbert Xu e = ep[i]; 252*2a598d0bSHerbert Xu c = BITS_PER_MPI_LIMB; 253*2a598d0bSHerbert Xu } 254*2a598d0bSHerbert Xu 255*2a598d0bSHerbert Xu /* We shifted MOD, the modulo reduction argument, left MOD_SHIFT_CNT 256*2a598d0bSHerbert Xu * steps. Adjust the result by reducing it with the original MOD. 257*2a598d0bSHerbert Xu * 258*2a598d0bSHerbert Xu * Also make sure the result is put in RES->d (where it already 259*2a598d0bSHerbert Xu * might be, see above). 260*2a598d0bSHerbert Xu */ 261*2a598d0bSHerbert Xu if (mod_shift_cnt) { 262*2a598d0bSHerbert Xu carry_limb = 263*2a598d0bSHerbert Xu mpihelp_lshift(res->d, rp, rsize, mod_shift_cnt); 264*2a598d0bSHerbert Xu rp = res->d; 265*2a598d0bSHerbert Xu if (carry_limb) { 266*2a598d0bSHerbert Xu rp[rsize] = carry_limb; 267*2a598d0bSHerbert Xu rsize++; 268*2a598d0bSHerbert Xu } 269*2a598d0bSHerbert Xu } else { 270*2a598d0bSHerbert Xu MPN_COPY(res->d, rp, rsize); 271*2a598d0bSHerbert Xu rp = res->d; 272*2a598d0bSHerbert Xu } 273*2a598d0bSHerbert Xu 274*2a598d0bSHerbert Xu if (rsize >= msize) { 275*2a598d0bSHerbert Xu mpihelp_divrem(rp + msize, 0, rp, rsize, mp, msize); 276*2a598d0bSHerbert Xu rsize = msize; 277*2a598d0bSHerbert Xu } 278*2a598d0bSHerbert Xu 279*2a598d0bSHerbert Xu /* Remove any leading zero words from the result. */ 280*2a598d0bSHerbert Xu if (mod_shift_cnt) 281*2a598d0bSHerbert Xu mpihelp_rshift(rp, rp, rsize, mod_shift_cnt); 282*2a598d0bSHerbert Xu MPN_NORMALIZE(rp, rsize); 283*2a598d0bSHerbert Xu } 284*2a598d0bSHerbert Xu 285*2a598d0bSHerbert Xu if (negative_result && rsize) { 286*2a598d0bSHerbert Xu if (mod_shift_cnt) 287*2a598d0bSHerbert Xu mpihelp_rshift(mp, mp, msize, mod_shift_cnt); 288*2a598d0bSHerbert Xu mpihelp_sub(rp, mp, msize, rp, rsize); 289*2a598d0bSHerbert Xu rsize = msize; 290*2a598d0bSHerbert Xu rsign = msign; 291*2a598d0bSHerbert Xu MPN_NORMALIZE(rp, rsize); 292*2a598d0bSHerbert Xu } 293*2a598d0bSHerbert Xu res->nlimbs = rsize; 294*2a598d0bSHerbert Xu res->sign = rsign; 295*2a598d0bSHerbert Xu 296*2a598d0bSHerbert Xu leave: 297*2a598d0bSHerbert Xu rc = 0; 298*2a598d0bSHerbert Xu enomem: 299*2a598d0bSHerbert Xu mpihelp_release_karatsuba_ctx(&karactx); 300*2a598d0bSHerbert Xu if (assign_rp) 301*2a598d0bSHerbert Xu mpi_assign_limb_space(res, rp, size); 302*2a598d0bSHerbert Xu if (mp_marker) 303*2a598d0bSHerbert Xu mpi_free_limb_space(mp_marker); 304*2a598d0bSHerbert Xu if (bp_marker) 305*2a598d0bSHerbert Xu mpi_free_limb_space(bp_marker); 306*2a598d0bSHerbert Xu if (ep_marker) 307*2a598d0bSHerbert Xu mpi_free_limb_space(ep_marker); 308*2a598d0bSHerbert Xu if (xp_marker) 309*2a598d0bSHerbert Xu mpi_free_limb_space(xp_marker); 310*2a598d0bSHerbert Xu if (tspace) 311*2a598d0bSHerbert Xu mpi_free_limb_space(tspace); 312*2a598d0bSHerbert Xu return rc; 313*2a598d0bSHerbert Xu } 314*2a598d0bSHerbert Xu EXPORT_SYMBOL_GPL(mpi_powm); 315