chiark / gitweb /
Change to use left-to-right bitwise exponentiation. This will improve
[catacomb] / mpbarrett.c
1 /* -*-c-*-
2  *
3  * $Id: mpbarrett.c,v 1.5 2000/07/29 17:04:33 mdw Exp $
4  *
5  * Barrett modular reduction
6  *
7  * (c) 1999 Straylight/Edgeware
8  */
9
10 /*----- Licensing notice --------------------------------------------------* 
11  *
12  * This file is part of Catacomb.
13  *
14  * Catacomb is free software; you can redistribute it and/or modify
15  * it under the terms of the GNU Library General Public License as
16  * published by the Free Software Foundation; either version 2 of the
17  * License, or (at your option) any later version.
18  * 
19  * Catacomb is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU Library General Public License for more details.
23  * 
24  * You should have received a copy of the GNU Library General Public
25  * License along with Catacomb; if not, write to the Free
26  * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
27  * MA 02111-1307, USA.
28  */
29
30 /*----- Revision history --------------------------------------------------* 
31  *
32  * $Log: mpbarrett.c,v $
33  * Revision 1.5  2000/07/29 17:04:33  mdw
34  * Change to use left-to-right bitwise exponentiation.  This will improve
35  * performance when the base is small.
36  *
37  * Revision 1.4  2000/06/17 11:45:09  mdw
38  * Major memory management overhaul.  Added arena support.  Use the secure
39  * arena for secret integers.  Replace and improve the MP management macros
40  * (e.g., replace MP_MODIFY by MP_DEST).
41  *
42  * Revision 1.3  1999/12/12 15:08:52  mdw
43  * Don't bother shifting %$q$% in @mpbarrett_reduce@, just skip the least
44  * significant digits.
45  *
46  * Revision 1.2  1999/12/11 01:50:56  mdw
47  * Improve initialization slightly.
48  *
49  * Revision 1.1  1999/12/10 23:21:59  mdw
50  * Barrett reduction support: works with even moduli.
51  *
52  */
53
54 /*----- Header files ------------------------------------------------------*/
55
56 #include "mp.h"
57 #include "mpbarrett.h"
58
59 /*----- Main code ---------------------------------------------------------*/
60
61 /* --- @mpbarrett_create@ --- *
62  *
63  * Arguments:   @mpbarrett *mb@ = pointer to Barrett reduction context
64  *              @mp *m@ = modulus to work to
65  *
66  *
67  * Returns:     ---
68  *
69  * Use:         Initializes a Barrett reduction context ready for use.
70  */
71
72 void mpbarrett_create(mpbarrett *mb, mp *m)
73 {
74   mp *b;
75
76   /* --- Validate the arguments --- */
77
78   assert(((void)"Barrett modulus must be positive", (m->f & MP_NEG) == 0));
79
80   /* --- Compute %$\mu$% --- */
81
82   mp_shrink(m);
83   mb->k = MP_LEN(m);
84   mb->m = MP_COPY(m);
85   b = mp_new(2 * mb->k + 1, 0);
86   MPX_ZERO(b->v, b->vl - 1);
87   b->vl[-1] = 1;
88   mp_div(&b, 0, b, m);
89   mb->mu = b;
90 }
91
92 /* --- @mpbarrett_destroy@ --- *
93  *
94  * Arguments:   @mpbarrett *mb@ = pointer to Barrett reduction context
95  *
96  * Returns:     ---
97  *
98  * Use:         Destroys a Barrett reduction context releasing any resources
99  *              claimed.
100  */
101
102 void mpbarrett_destroy(mpbarrett *mb)
103 {
104   mp_drop(mb->m);
105   mp_drop(mb->mu);
106 }
107
108 /* --- @mpbarrett_reduce@ --- *
109  *
110  * Arguments:   @mpbarrett *mb@ = pointer to Barrett reduction context
111  *              @mp *d@ = destination for result
112  *              @mp *m@ = number to reduce
113  *
114  * Returns:     The residue of @m@ modulo the number in the reduction
115  *              context.
116  *
117  * Use:         Performs an efficient modular reduction.  The argument is
118  *              assumed to be positive.
119  */
120
121 mp *mpbarrett_reduce(mpbarrett *mb, mp *d, mp *m)
122 {
123   mp *q;
124   size_t k = mb->k;
125
126   /* --- Special case if @m@ is too small --- */
127
128   if (MP_LEN(m) < k) {
129     m = MP_COPY(m);
130     if (d)
131       MP_DROP(d);
132     return (m);
133   }
134
135   /* --- First stage --- */
136
137   {
138     mp qq;
139     mp_build(&qq, m->v + (k - 1), m->vl);
140     q = mp_mul(MP_NEW, &qq, mb->mu);
141     if (MP_LEN(q) <= k) {
142       m = MP_COPY(m);
143       if (d)
144         MP_DROP(d);
145       return (m);
146     }
147   }
148
149   /* --- Second stage --- */
150
151   {
152     mp *r;
153     mpw *mvl;
154
155     MP_COPY(m);
156     if (MP_LEN(m) <= k + 1)
157       mvl = m->vl;
158     else
159       mvl = m->v + k + 1;
160     r = mp_new(k + 1, (q->f | mb->m->f) & MP_BURN);
161     mpx_umul(r->v, r->vl, q->v + k + 1, q->vl, mb->m->v, mb->m->vl);
162     MP_DEST(d, k + 1, r->f);
163     mpx_usub(d->v, d->vl, m->v, mvl, r->v, r->vl);
164     d->f = (m->f | r->f) & MP_BURN;
165     MP_DROP(r);
166     MP_DROP(q);
167     MP_DROP(m);
168   }
169
170   /* --- Final stage --- */
171
172   MP_SHRINK(d);
173   while (MPX_UCMP(d->v, d->vl, >=, mb->m->v, mb->m->vl))
174     mpx_usub(d->v, d->vl, d->v, d->vl, mb->m->v, mb->m->vl);
175
176   MP_SHRINK(d);
177   return (d);
178 }
179
180 /* --- @mpbarrett_exp@ --- *
181  *
182  * Arguments:   @mpbarrett *mb@ = pointer to Barrett reduction context
183  *              @mp *d@ = fake destination
184  *              @mp *a@ = base
185  *              @mp *e@ = exponent
186  *
187  * Returns:     Result, %$a^e \bmod m$%.
188  */
189
190 mp *mpbarrett_exp(mpbarrett *mb, mp *d, mp *a, mp *e)
191 {
192   mpscan sc;
193   mp *x = MP_ONE;
194   mp *spare = (e->f & MP_BURN) ? MP_NEWSEC : MP_NEW;
195   unsigned sq = 0;
196
197   a = MP_COPY(a);
198   mp_rscan(&sc, e);
199   if (!MP_RSTEP(&sc))
200     goto exit;
201   while (!MP_RBIT(&sc))
202     MP_RSTEP(&sc);
203
204   /* --- Do the main body of the work --- */
205
206   for (;;) {
207     sq++;
208     while (sq) {
209       mp *y;
210       y = mp_sqr(spare, x);
211       y = mpbarrett_reduce(mb, y, y);
212       spare = x; x = y;
213       sq--;
214     }
215     {
216       mp *y = mp_mul(spare, x, a);
217       y = mpbarrett_reduce(mb, y, y);
218       spare = x; x = y;
219     }
220     for (;;) {
221       if (!MP_RSTEP(&sc))
222         goto done;
223       if (MP_RBIT(&sc))
224         break;
225       sq++;
226     }
227   }
228
229   /* --- Do a final round of squaring --- */
230
231 done:
232   while (sq) {
233     mp *y;
234     y = mp_sqr(spare, x);
235     y = mpbarrett_reduce(mb, y, y);
236     spare = x; x = y;
237     sq--;
238   }  
239
240 exit:
241   MP_DROP(a);
242   if (spare != MP_NEW)
243     MP_DROP(spare);
244   if (d != MP_NEW)
245     MP_DROP(d);
246   return (x);
247 }
248
249 /*----- Test rig ----------------------------------------------------------*/
250
251 #ifdef TEST_RIG
252
253 static int vmod(dstr *v)
254 {
255   mp *x = *(mp **)v[0].buf;
256   mp *n = *(mp **)v[1].buf;
257   mp *r = *(mp **)v[2].buf;
258   mp *s;
259   mpbarrett mb;
260   int ok = 1;
261
262   mpbarrett_create(&mb, n);
263   s = mpbarrett_reduce(&mb, MP_NEW, x);
264
265   if (MP_CMP(s, !=, r)) {
266     fputs("\n*** barrett reduction failure\n", stderr);
267     fputs("x = ", stderr); mp_writefile(x, stderr, 10); fputc('\n', stderr);
268     fputs("n = ", stderr); mp_writefile(n, stderr, 10); fputc('\n', stderr);
269     fputs("r = ", stderr); mp_writefile(r, stderr, 10); fputc('\n', stderr);
270     fputs("s = ", stderr); mp_writefile(s, stderr, 10); fputc('\n', stderr);
271     ok = 0;
272   }
273
274   mpbarrett_destroy(&mb);
275   mp_drop(x);
276   mp_drop(n);
277   mp_drop(r);
278   mp_drop(s);
279   assert(mparena_count(MPARENA_GLOBAL) == 0);
280   return (ok);
281 }
282
283 static int vexp(dstr *v)
284 {
285   mp *m = *(mp **)v[0].buf;
286   mp *a = *(mp **)v[1].buf;
287   mp *b = *(mp **)v[2].buf;
288   mp *r = *(mp **)v[3].buf;
289   mp *mr;
290   int ok = 1;
291
292   mpbarrett mb;
293   mpbarrett_create(&mb, m);
294
295   mr = mpbarrett_exp(&mb, MP_NEW, a, b);
296
297   if (MP_CMP(mr, !=, r)) {
298     fputs("\n*** barrett modexp failed", stderr);
299     fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
300     fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
301     fputs("\n e = ", stderr); mp_writefile(b, stderr, 10);
302     fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
303     fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
304     fputc('\n', stderr);
305     ok = 0;
306   }
307
308   mp_drop(m);
309   mp_drop(a);
310   mp_drop(b);
311   mp_drop(r);
312   mp_drop(mr);
313   mpbarrett_destroy(&mb);
314   assert(mparena_count(MPARENA_GLOBAL) == 0);
315   return ok;
316 }
317
318 static test_chunk tests[] = {
319   { "mpbarrett-reduce", vmod, { &type_mp, &type_mp, &type_mp, 0 } },
320   { "mpbarrett-exp", vexp, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
321   { 0, 0, { 0 } }
322 };
323
324 int main(int argc, char *argv[])
325 {
326   sub_init();
327   test_run(argc, argv, tests, SRCDIR "/tests/mpbarrett");
328   return (0);
329 }
330
331 #endif
332
333 /*----- That's all, folks -------------------------------------------------*/