Avoid trashing arguments before we've used them.
[u/mdw/catacomb] / mpmont.c
1 /* -*-c-*-
2 *
3 * $Id: mpmont.c,v 1.16 2002/01/13 13:40:31 mdw Exp $
4 *
5 * Montgomery 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: mpmont.c,v $
33 * Revision 1.16 2002/01/13 13:40:31 mdw
34 * Avoid trashing arguments before we've used them.
35 *
36 * Revision 1.15 2001/06/16 13:00:20 mdw
37 * Use the generic exponentiation functions.
38 *
39 * Revision 1.14 2001/02/22 09:04:26 mdw
40 * Cosmetic fix.
41 *
42 * Revision 1.13 2001/02/03 12:00:29 mdw
43 * Now @mp_drop@ checks its argument is non-NULL before attempting to free
44 * it. Note that the macro version @MP_DROP@ doesn't do this.
45 *
46 * Revision 1.12 2000/10/08 15:48:35 mdw
47 * Rename Karatsuba constants now that we have @gfx_kmul@ too.
48 *
49 * Revision 1.11 2000/10/08 12:04:27 mdw
50 * (mpmont_reduce, mpmont_mul): Cope with negative numbers.
51 *
52 * Revision 1.10 2000/07/29 17:05:43 mdw
53 * (mpmont_expr): Use sliding window exponentiation, with a drop-through
54 * for small exponents to use a simple left-to-right bitwise routine. This
55 * can reduce modexp times by up to a quarter.
56 *
57 * Revision 1.9 2000/06/17 11:45:09 mdw
58 * Major memory management overhaul. Added arena support. Use the secure
59 * arena for secret integers. Replace and improve the MP management macros
60 * (e.g., replace MP_MODIFY by MP_DEST).
61 *
62 * Revision 1.8 1999/12/22 15:55:00 mdw
63 * Adjust Karatsuba parameters.
64 *
65 * Revision 1.7 1999/12/11 01:51:14 mdw
66 * Use a Karatsuba-based reduction for large moduli.
67 *
68 * Revision 1.6 1999/12/10 23:18:39 mdw
69 * Change interface for suggested destinations.
70 *
71 * Revision 1.5 1999/11/22 13:58:40 mdw
72 * Add an option to disable Montgomery reduction, so that performance
73 * comparisons can be done.
74 *
75 * Revision 1.4 1999/11/21 12:27:06 mdw
76 * Remove a division from the Montgomery setup by calculating
77 * %$R^2 \bmod m$% first and then %$R \bmod m$% by Montgomery reduction of
78 * %$R^2$%.
79 *
80 * Revision 1.3 1999/11/21 11:35:10 mdw
81 * Performance improvement: use @mp_sqr@ and @mpmont_reduce@ instead of
82 * @mpmont_mul@ for squaring in exponentiation.
83 *
84 * Revision 1.2 1999/11/19 13:17:26 mdw
85 * Add extra interface to exponentiation which returns a Montgomerized
86 * result.
87 *
88 * Revision 1.1 1999/11/17 18:02:16 mdw
89 * New multiprecision integer arithmetic suite.
90 *
91 */
92
93 /*----- Header files ------------------------------------------------------*/
94
95 #include "mp.h"
96 #include "mpmont.h"
97 #include "mpmont-exp.h"
98
99 /*----- Tweakables --------------------------------------------------------*/
100
101 /* --- @MPMONT_DISABLE@ --- *
102 *
103 * Replace all the clever Montgomery reduction with good old-fashioned long
104 * division.
105 */
106
107 /* #define MPMONT_DISABLE */
108
109 /*----- Reduction and multiplication --------------------------------------*/
110
111 /* --- @mpmont_create@ --- *
112 *
113 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
114 * @mp *m@ = modulus to use
115 *
116 * Returns: ---
117 *
118 * Use: Initializes a Montgomery reduction context ready for use.
119 * The argument @m@ must be a positive odd integer.
120 */
121
122 #ifdef MPMONT_DISABLE
123
124 void mpmont_create(mpmont *mm, mp *m)
125 {
126 mp_shrink(m);
127 mm->m = MP_COPY(m);
128 mm->r = MP_ONE;
129 mm->r2 = MP_ONE;
130 mm->mi = MP_ONE;
131 }
132
133 #else
134
135 void mpmont_create(mpmont *mm, mp *m)
136 {
137 size_t n = MP_LEN(m);
138 mp *r2 = mp_new(2 * n + 1, 0);
139 mp r;
140
141 /* --- Validate the arguments --- */
142
143 assert(((void)"Montgomery modulus must be positive",
144 (m->f & MP_NEG) == 0));
145 assert(((void)"Montgomery modulus must be odd", m->v[0] & 1));
146
147 /* --- Take a copy of the modulus --- */
148
149 mp_shrink(m);
150 mm->m = MP_COPY(m);
151
152 /* --- Determine %$R^2$% --- */
153
154 mm->n = n;
155 MPX_ZERO(r2->v, r2->vl - 1);
156 r2->vl[-1] = 1;
157
158 /* --- Find the magic value @mi@ --- */
159
160 mp_build(&r, r2->v + n, r2->vl);
161 mm->mi = MP_NEW;
162 mp_gcd(0, 0, &mm->mi, &r, m);
163 mm->mi = mp_sub(mm->mi, &r, mm->mi);
164
165 /* --- Discover the values %$R \bmod m$% and %$R^2 \bmod m$% --- */
166
167 mm->r2 = MP_NEW;
168 mp_div(0, &mm->r2, r2, m);
169 mm->r = mpmont_reduce(mm, MP_NEW, mm->r2);
170 MP_DROP(r2);
171 }
172
173 #endif
174
175 /* --- @mpmont_destroy@ --- *
176 *
177 * Arguments: @mpmont *mm@ = pointer to a Montgomery reduction context
178 *
179 * Returns: ---
180 *
181 * Use: Disposes of a context when it's no longer of any use to
182 * anyone.
183 */
184
185 void mpmont_destroy(mpmont *mm)
186 {
187 MP_DROP(mm->m);
188 MP_DROP(mm->r);
189 MP_DROP(mm->r2);
190 MP_DROP(mm->mi);
191 }
192
193 /* --- @mpmont_reduce@ --- *
194 *
195 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
196 * @mp *d@ = destination
197 * @mp *a@ = source, assumed positive
198 *
199 * Returns: Result, %$a R^{-1} \bmod m$%.
200 */
201
202 #ifdef MPMONT_DISABLE
203
204 mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
205 {
206 mp_div(0, &d, a, mm->m);
207 return (d);
208 }
209
210 #else
211
212 mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
213 {
214 size_t n = mm->n;
215
216 /* --- Check for serious Karatsuba reduction --- */
217
218 if (n > MPK_THRESH * 3) {
219 mp al;
220 mpw *vl;
221 mp *u;
222
223 if (MP_LEN(a) >= n)
224 vl = a->v + n;
225 else
226 vl = a->vl;
227 mp_build(&al, a->v, vl);
228 u = mp_mul(MP_NEW, &al, mm->mi);
229 if (MP_LEN(u) > n)
230 u->vl = u->v + n;
231 u = mp_mul(u, u, mm->m);
232 d = mp_add(d, a, u);
233 mp_drop(u);
234 }
235
236 /* --- Otherwise do it the hard way --- */
237
238 else {
239 mpw *dv, *dvl;
240 mpw *mv, *mvl;
241 mpw mi;
242 size_t k = n;
243
244 /* --- Initial conditioning of the arguments --- */
245
246 a = MP_COPY(a);
247 if (d)
248 MP_DROP(d);
249 d = a;
250 MP_DEST(d, 2 * n + 1, a->f);
251
252 dv = d->v; dvl = d->vl;
253 mv = mm->m->v; mvl = mm->m->vl;
254
255 /* --- Let's go to work --- */
256
257 mi = mm->mi->v[0];
258 while (k--) {
259 mpw u = MPW(*dv * mi);
260 MPX_UMLAN(dv, dvl, mv, mvl, u);
261 dv++;
262 }
263 }
264
265 /* --- Wrap everything up --- */
266
267 memmove(d->v, d->v + n, MPWS(MP_LEN(d) - n));
268 d->vl -= n;
269 if (MPX_UCMP(d->v, d->vl, >=, mm->m->v, mm->m->vl))
270 mpx_usub(d->v, d->vl, d->v, d->vl, mm->m->v, mm->m->vl);
271 if (d->f & MP_NEG) {
272 mpx_usub(d->v, d->vl, mm->m->v, mm->m->vl, d->v, d->vl);
273 d->f &= ~MP_NEG;
274 }
275 MP_SHRINK(d);
276 return (d);
277 }
278
279 #endif
280
281 /* --- @mpmont_mul@ --- *
282 *
283 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
284 * @mp *d@ = destination
285 * @mp *a, *b@ = sources, assumed positive
286 *
287 * Returns: Result, %$a b R^{-1} \bmod m$%.
288 */
289
290 #ifdef MPMONT_DISABLE
291
292 mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
293 {
294 d = mp_mul(d, a, b);
295 mp_div(0, &d, d, mm->m);
296 return (d);
297 }
298
299 #else
300
301 mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
302 {
303 if (mm->n > MPK_THRESH * 3) {
304 d = mp_mul(d, a, b);
305 d = mpmont_reduce(mm, d, d);
306 } else {
307 mpw *dv, *dvl;
308 mpw *av, *avl;
309 mpw *bv, *bvl;
310 mpw *mv, *mvl;
311 mpw y;
312 size_t n, i;
313 mpw mi;
314
315 /* --- Initial conditioning of the arguments --- */
316
317 if (MP_LEN(a) > MP_LEN(b)) {
318 mp *t = a; a = b; b = t;
319 }
320 n = MP_LEN(mm->m);
321
322 a = MP_COPY(a);
323 b = MP_COPY(b);
324 MP_DEST(d, 2 * n + 1, a->f | b->f | MP_UNDEF);
325 dv = d->v; dvl = d->vl;
326 MPX_ZERO(dv, dvl);
327 av = a->v; avl = a->vl;
328 bv = b->v; bvl = b->vl;
329 mv = mm->m->v; mvl = mm->m->vl;
330 y = *bv;
331
332 /* --- Montgomery multiplication phase --- */
333
334 i = 0;
335 mi = mm->mi->v[0];
336 while (i < n && av < avl) {
337 mpw x = *av++;
338 mpw u = MPW((*dv + x * y) * mi);
339 MPX_UMLAN(dv, dvl, bv, bvl, x);
340 MPX_UMLAN(dv, dvl, mv, mvl, u);
341 dv++;
342 i++;
343 }
344
345 /* --- Simpler Montgomery reduction phase --- */
346
347 while (i < n) {
348 mpw u = MPW(*dv * mi);
349 MPX_UMLAN(dv, dvl, mv, mvl, u);
350 dv++;
351 i++;
352 }
353
354 /* --- Done --- */
355
356 memmove(d->v, dv, MPWS(dvl - dv));
357 d->vl -= dv - d->v;
358 if (MPX_UCMP(d->v, d->vl, >=, mm->m->v, mm->m->vl))
359 mpx_usub(d->v, d->vl, d->v, d->vl, mm->m->v, mm->m->vl);
360 if ((a->f ^ b->f) & MP_NEG)
361 mpx_usub(d->v, d->vl, mm->m->v, mm->m->vl, d->v, d->vl);
362 MP_SHRINK(d);
363 d->f = (a->f | b->f) & MP_BURN;
364 MP_DROP(a);
365 MP_DROP(b);
366 }
367
368 return (d);
369 }
370
371 #endif
372
373 /*----- Exponentiation ----------------------------------------------------*/
374
375 /* --- @mpmont_expr@ --- *
376 *
377 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
378 * @mp *d@ = fake destination
379 * @mp *a@ = base
380 * @mp *e@ = exponent
381 *
382 * Returns: Result, %$(a R^{-1})^e R \bmod m$%.
383 */
384
385 mp *mpmont_expr(mpmont *mm, mp *d, mp *a, mp *e)
386 {
387 mp *x = MP_COPY(mm->r);
388 mp *spare = (e->f & MP_BURN) ? MP_NEWSEC : MP_NEW;
389
390 MP_SHRINK(e);
391 if (MP_LEN(e) == 0)
392 ;
393 else if (MP_LEN(e) < EXP_THRESH)
394 EXP_SIMPLE(x, a, e);
395 else
396 EXP_WINDOW(x, a, e);
397 mp_drop(d);
398 mp_drop(spare);
399 return (x);
400 }
401
402 /* --- @mpmont_exp@ --- *
403 *
404 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
405 * @mp *d@ = fake destination
406 * @mp *a@ = base
407 * @mp *e@ = exponent
408 *
409 * Returns: Result, %$a^e \bmod m$%.
410 */
411
412 mp *mpmont_exp(mpmont *mm, mp *d, mp *a, mp *e)
413 {
414 e = MP_COPY(e);
415 d = mpmont_mul(mm, d, a, mm->r2);
416 d = mpmont_expr(mm, d, d, e);
417 d = mpmont_reduce(mm, d, d);
418 MP_DROP(e);
419 return (d);
420 }
421
422 /*----- Test rig ----------------------------------------------------------*/
423
424 #ifdef TEST_RIG
425
426 static int tcreate(dstr *v)
427 {
428 mp *m = *(mp **)v[0].buf;
429 mp *mi = *(mp **)v[1].buf;
430 mp *r = *(mp **)v[2].buf;
431 mp *r2 = *(mp **)v[3].buf;
432
433 mpmont mm;
434 int ok = 1;
435
436 mpmont_create(&mm, m);
437
438 if (mm.mi->v[0] != mi->v[0]) {
439 fprintf(stderr, "\n*** bad mi: found %lu, expected %lu",
440 (unsigned long)mm.mi->v[0], (unsigned long)mi->v[0]);
441 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
442 fputc('\n', stderr);
443 ok = 0;
444 }
445
446 if (!MP_EQ(mm.r, r)) {
447 fputs("\n*** bad r", stderr);
448 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
449 fputs("\nexpected ", stderr); mp_writefile(r, stderr, 10);
450 fputs("\n found ", stderr); mp_writefile(mm.r, stderr, 10);
451 fputc('\n', stderr);
452 ok = 0;
453 }
454
455 if (!MP_EQ(mm.r2, r2)) {
456 fputs("\n*** bad r2", stderr);
457 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
458 fputs("\nexpected ", stderr); mp_writefile(r2, stderr, 10);
459 fputs("\n found ", stderr); mp_writefile(mm.r2, stderr, 10);
460 fputc('\n', stderr);
461 ok = 0;
462 }
463
464 MP_DROP(m);
465 MP_DROP(mi);
466 MP_DROP(r);
467 MP_DROP(r2);
468 mpmont_destroy(&mm);
469 assert(mparena_count(MPARENA_GLOBAL) == 0);
470 return (ok);
471 }
472
473 static int tmul(dstr *v)
474 {
475 mp *m = *(mp **)v[0].buf;
476 mp *a = *(mp **)v[1].buf;
477 mp *b = *(mp **)v[2].buf;
478 mp *r = *(mp **)v[3].buf;
479 int ok = 1;
480
481 mpmont mm;
482 mpmont_create(&mm, m);
483
484 {
485 mp *qr = mp_mul(MP_NEW, a, b);
486 mp_div(0, &qr, qr, m);
487
488 if (!MP_EQ(qr, r)) {
489 fputs("\n*** classical modmul failed", stderr);
490 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
491 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
492 fputs("\n b = ", stderr); mp_writefile(b, stderr, 10);
493 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
494 fputs("\nqr = ", stderr); mp_writefile(qr, stderr, 10);
495 fputc('\n', stderr);
496 ok = 0;
497 }
498
499 mp_drop(qr);
500 }
501
502 {
503 mp *ar = mpmont_mul(&mm, MP_NEW, a, mm.r2);
504 mp *br = mpmont_mul(&mm, MP_NEW, b, mm.r2);
505 mp *mr = mpmont_mul(&mm, MP_NEW, ar, br);
506 mr = mpmont_reduce(&mm, mr, mr);
507 if (!MP_EQ(mr, r)) {
508 fputs("\n*** montgomery modmul failed", stderr);
509 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
510 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
511 fputs("\n b = ", stderr); mp_writefile(b, stderr, 10);
512 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
513 fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
514 fputc('\n', stderr);
515 ok = 0;
516 }
517 MP_DROP(ar); MP_DROP(br);
518 mp_drop(mr);
519 }
520
521
522 MP_DROP(m);
523 MP_DROP(a);
524 MP_DROP(b);
525 MP_DROP(r);
526 mpmont_destroy(&mm);
527 assert(mparena_count(MPARENA_GLOBAL) == 0);
528 return ok;
529 }
530
531 static int texp(dstr *v)
532 {
533 mp *m = *(mp **)v[0].buf;
534 mp *a = *(mp **)v[1].buf;
535 mp *b = *(mp **)v[2].buf;
536 mp *r = *(mp **)v[3].buf;
537 mp *mr;
538 int ok = 1;
539
540 mpmont mm;
541 mpmont_create(&mm, m);
542
543 mr = mpmont_exp(&mm, MP_NEW, a, b);
544
545 if (!MP_EQ(mr, r)) {
546 fputs("\n*** montgomery modexp failed", stderr);
547 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
548 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
549 fputs("\n e = ", stderr); mp_writefile(b, stderr, 10);
550 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
551 fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
552 fputc('\n', stderr);
553 ok = 0;
554 }
555
556 MP_DROP(m);
557 MP_DROP(a);
558 MP_DROP(b);
559 MP_DROP(r);
560 MP_DROP(mr);
561 mpmont_destroy(&mm);
562 assert(mparena_count(MPARENA_GLOBAL) == 0);
563 return ok;
564 }
565
566
567 static test_chunk tests[] = {
568 { "create", tcreate, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
569 { "mul", tmul, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
570 { "exp", texp, { &type_mp, &type_mp, &type_mp, &type_mp, 0 } },
571 { 0, 0, { 0 } },
572 };
573
574 int main(int argc, char *argv[])
575 {
576 sub_init();
577 test_run(argc, argv, tests, SRCDIR "/tests/mpmont");
578 return (0);
579 }
580
581 #endif
582
583 /*----- That's all, folks -------------------------------------------------*/