0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016 #include <linux/sched.h>
0017 #include <linux/string.h>
0018 #include "mpi-internal.h"
0019 #include "longlong.h"
0020
0021
0022
0023
0024 int mpi_powm(MPI res, MPI base, MPI exp, MPI mod)
0025 {
0026 mpi_ptr_t mp_marker = NULL, bp_marker = NULL, ep_marker = NULL;
0027 struct karatsuba_ctx karactx = {};
0028 mpi_ptr_t xp_marker = NULL;
0029 mpi_ptr_t tspace = NULL;
0030 mpi_ptr_t rp, ep, mp, bp;
0031 mpi_size_t esize, msize, bsize, rsize;
0032 int msign, bsign, rsign;
0033 mpi_size_t size;
0034 int mod_shift_cnt;
0035 int negative_result;
0036 int assign_rp = 0;
0037 mpi_size_t tsize = 0;
0038
0039 int rc = -ENOMEM;
0040
0041 esize = exp->nlimbs;
0042 msize = mod->nlimbs;
0043 size = 2 * msize;
0044 msign = mod->sign;
0045
0046 rp = res->d;
0047 ep = exp->d;
0048
0049 if (!msize)
0050 return -EINVAL;
0051
0052 if (!esize) {
0053
0054
0055 res->nlimbs = (msize == 1 && mod->d[0] == 1) ? 0 : 1;
0056 if (res->nlimbs) {
0057 if (mpi_resize(res, 1) < 0)
0058 goto enomem;
0059 rp = res->d;
0060 rp[0] = 1;
0061 }
0062 res->sign = 0;
0063 goto leave;
0064 }
0065
0066
0067
0068
0069
0070 mp = mp_marker = mpi_alloc_limb_space(msize);
0071 if (!mp)
0072 goto enomem;
0073 mod_shift_cnt = count_leading_zeros(mod->d[msize - 1]);
0074 if (mod_shift_cnt)
0075 mpihelp_lshift(mp, mod->d, msize, mod_shift_cnt);
0076 else
0077 MPN_COPY(mp, mod->d, msize);
0078
0079 bsize = base->nlimbs;
0080 bsign = base->sign;
0081 if (bsize > msize) {
0082
0083
0084 bp = bp_marker = mpi_alloc_limb_space(bsize + 1);
0085 if (!bp)
0086 goto enomem;
0087 MPN_COPY(bp, base->d, bsize);
0088
0089
0090 mpihelp_divrem(bp + msize, 0, bp, bsize, mp, msize);
0091 bsize = msize;
0092
0093
0094 MPN_NORMALIZE(bp, bsize);
0095 } else
0096 bp = base->d;
0097
0098 if (!bsize) {
0099 res->nlimbs = 0;
0100 res->sign = 0;
0101 goto leave;
0102 }
0103
0104 if (res->alloced < size) {
0105
0106
0107
0108 if (rp == ep || rp == mp || rp == bp) {
0109 rp = mpi_alloc_limb_space(size);
0110 if (!rp)
0111 goto enomem;
0112 assign_rp = 1;
0113 } else {
0114 if (mpi_resize(res, size) < 0)
0115 goto enomem;
0116 rp = res->d;
0117 }
0118 } else {
0119 if (rp == bp) {
0120
0121 BUG_ON(bp_marker);
0122 bp = bp_marker = mpi_alloc_limb_space(bsize);
0123 if (!bp)
0124 goto enomem;
0125 MPN_COPY(bp, rp, bsize);
0126 }
0127 if (rp == ep) {
0128
0129 ep = ep_marker = mpi_alloc_limb_space(esize);
0130 if (!ep)
0131 goto enomem;
0132 MPN_COPY(ep, rp, esize);
0133 }
0134 if (rp == mp) {
0135
0136 BUG_ON(mp_marker);
0137 mp = mp_marker = mpi_alloc_limb_space(msize);
0138 if (!mp)
0139 goto enomem;
0140 MPN_COPY(mp, rp, msize);
0141 }
0142 }
0143
0144 MPN_COPY(rp, bp, bsize);
0145 rsize = bsize;
0146 rsign = bsign;
0147
0148 {
0149 mpi_size_t i;
0150 mpi_ptr_t xp;
0151 int c;
0152 mpi_limb_t e;
0153 mpi_limb_t carry_limb;
0154
0155 xp = xp_marker = mpi_alloc_limb_space(2 * (msize + 1));
0156 if (!xp)
0157 goto enomem;
0158
0159 negative_result = (ep[0] & 1) && base->sign;
0160
0161 i = esize - 1;
0162 e = ep[i];
0163 c = count_leading_zeros(e);
0164 e = (e << c) << 1;
0165 c = BITS_PER_MPI_LIMB - 1 - c;
0166
0167
0168
0169
0170
0171
0172
0173
0174
0175
0176
0177 for (;;) {
0178 while (c) {
0179 mpi_ptr_t tp;
0180 mpi_size_t xsize;
0181
0182
0183 if (rsize < KARATSUBA_THRESHOLD)
0184 mpih_sqr_n_basecase(xp, rp, rsize);
0185 else {
0186 if (!tspace) {
0187 tsize = 2 * rsize;
0188 tspace =
0189 mpi_alloc_limb_space(tsize);
0190 if (!tspace)
0191 goto enomem;
0192 } else if (tsize < (2 * rsize)) {
0193 mpi_free_limb_space(tspace);
0194 tsize = 2 * rsize;
0195 tspace =
0196 mpi_alloc_limb_space(tsize);
0197 if (!tspace)
0198 goto enomem;
0199 }
0200 mpih_sqr_n(xp, rp, rsize, tspace);
0201 }
0202
0203 xsize = 2 * rsize;
0204 if (xsize > msize) {
0205 mpihelp_divrem(xp + msize, 0, xp, xsize,
0206 mp, msize);
0207 xsize = msize;
0208 }
0209
0210 tp = rp;
0211 rp = xp;
0212 xp = tp;
0213 rsize = xsize;
0214
0215 if ((mpi_limb_signed_t) e < 0) {
0216
0217 if (bsize < KARATSUBA_THRESHOLD) {
0218 mpi_limb_t tmp;
0219 if (mpihelp_mul
0220 (xp, rp, rsize, bp, bsize,
0221 &tmp) < 0)
0222 goto enomem;
0223 } else {
0224 if (mpihelp_mul_karatsuba_case
0225 (xp, rp, rsize, bp, bsize,
0226 &karactx) < 0)
0227 goto enomem;
0228 }
0229
0230 xsize = rsize + bsize;
0231 if (xsize > msize) {
0232 mpihelp_divrem(xp + msize, 0,
0233 xp, xsize, mp,
0234 msize);
0235 xsize = msize;
0236 }
0237
0238 tp = rp;
0239 rp = xp;
0240 xp = tp;
0241 rsize = xsize;
0242 }
0243 e <<= 1;
0244 c--;
0245 cond_resched();
0246 }
0247
0248 i--;
0249 if (i < 0)
0250 break;
0251 e = ep[i];
0252 c = BITS_PER_MPI_LIMB;
0253 }
0254
0255
0256
0257
0258
0259
0260
0261 if (mod_shift_cnt) {
0262 carry_limb =
0263 mpihelp_lshift(res->d, rp, rsize, mod_shift_cnt);
0264 rp = res->d;
0265 if (carry_limb) {
0266 rp[rsize] = carry_limb;
0267 rsize++;
0268 }
0269 } else {
0270 MPN_COPY(res->d, rp, rsize);
0271 rp = res->d;
0272 }
0273
0274 if (rsize >= msize) {
0275 mpihelp_divrem(rp + msize, 0, rp, rsize, mp, msize);
0276 rsize = msize;
0277 }
0278
0279
0280 if (mod_shift_cnt)
0281 mpihelp_rshift(rp, rp, rsize, mod_shift_cnt);
0282 MPN_NORMALIZE(rp, rsize);
0283 }
0284
0285 if (negative_result && rsize) {
0286 if (mod_shift_cnt)
0287 mpihelp_rshift(mp, mp, msize, mod_shift_cnt);
0288 mpihelp_sub(rp, mp, msize, rp, rsize);
0289 rsize = msize;
0290 rsign = msign;
0291 MPN_NORMALIZE(rp, rsize);
0292 }
0293 res->nlimbs = rsize;
0294 res->sign = rsign;
0295
0296 leave:
0297 rc = 0;
0298 enomem:
0299 mpihelp_release_karatsuba_ctx(&karactx);
0300 if (assign_rp)
0301 mpi_assign_limb_space(res, rp, size);
0302 if (mp_marker)
0303 mpi_free_limb_space(mp_marker);
0304 if (bp_marker)
0305 mpi_free_limb_space(bp_marker);
0306 if (ep_marker)
0307 mpi_free_limb_space(ep_marker);
0308 if (xp_marker)
0309 mpi_free_limb_space(xp_marker);
0310 if (tspace)
0311 mpi_free_limb_space(tspace);
0312 return rc;
0313 }
0314 EXPORT_SYMBOL_GPL(mpi_powm);