Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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