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