More efficient Rabin-Miller test: with random witnesses, skip redundant
[u/mdw/catacomb] / mpmont.c
CommitLineData
d3409d5e 1/* -*-c-*-
2 *
97490e68 3 * $Id: mpmont.c,v 1.16 2002/01/13 13:40:31 mdw Exp $
d3409d5e 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 $
97490e68 33 * Revision 1.16 2002/01/13 13:40:31 mdw
34 * Avoid trashing arguments before we've used them.
35 *
4640a0dd 36 * Revision 1.15 2001/06/16 13:00:20 mdw
37 * Use the generic exponentiation functions.
38 *
b096ec7f 39 * Revision 1.14 2001/02/22 09:04:26 mdw
40 * Cosmetic fix.
41 *
f1140c41 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 *
52cdaca9 46 * Revision 1.12 2000/10/08 15:48:35 mdw
47 * Rename Karatsuba constants now that we have @gfx_kmul@ too.
48 *
032099d1 49 * Revision 1.11 2000/10/08 12:04:27 mdw
50 * (mpmont_reduce, mpmont_mul): Cope with negative numbers.
51 *
c9d4c30b 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 *
d34decd2 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 *
01f6ed1a 62 * Revision 1.8 1999/12/22 15:55:00 mdw
63 * Adjust Karatsuba parameters.
64 *
f5f35081 65 * Revision 1.7 1999/12/11 01:51:14 mdw
66 * Use a Karatsuba-based reduction for large moduli.
67 *
ef5f4810 68 * Revision 1.6 1999/12/10 23:18:39 mdw
69 * Change interface for suggested destinations.
70 *
52e4b041 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 *
93feaa6e 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 *
79a34029 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 *
17ad212e 84 * Revision 1.2 1999/11/19 13:17:26 mdw
85 * Add extra interface to exponentiation which returns a Montgomerized
86 * result.
87 *
d3409d5e 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"
4640a0dd 97#include "mpmont-exp.h"
d3409d5e 98
52e4b041 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
4640a0dd 109/*----- Reduction and multiplication --------------------------------------*/
d3409d5e 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.
ef5f4810 119 * The argument @m@ must be a positive odd integer.
d3409d5e 120 */
121
52e4b041 122#ifdef MPMONT_DISABLE
123
124void 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;
f5f35081 130 mm->mi = MP_ONE;
52e4b041 131}
132
133#else
134
d3409d5e 135void mpmont_create(mpmont *mm, mp *m)
136{
f5f35081 137 size_t n = MP_LEN(m);
d34decd2 138 mp *r2 = mp_new(2 * n + 1, 0);
f5f35081 139 mp r;
140
ef5f4810 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
d3409d5e 147 /* --- Take a copy of the modulus --- */
148
149 mp_shrink(m);
150 mm->m = MP_COPY(m);
151
f5f35081 152 /* --- Determine %$R^2$% --- */
d3409d5e 153
f5f35081 154 mm->n = n;
155 MPX_ZERO(r2->v, r2->vl - 1);
156 r2->vl[-1] = 1;
d3409d5e 157
f5f35081 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);
d3409d5e 164
165 /* --- Discover the values %$R \bmod m$% and %$R^2 \bmod m$% --- */
166
f5f35081 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);
d3409d5e 171}
172
52e4b041 173#endif
174
d3409d5e 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
185void mpmont_destroy(mpmont *mm)
186{
187 MP_DROP(mm->m);
188 MP_DROP(mm->r);
189 MP_DROP(mm->r2);
f5f35081 190 MP_DROP(mm->mi);
d3409d5e 191}
192
193/* --- @mpmont_reduce@ --- *
194 *
195 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
196 * @mp *d@ = destination
ef5f4810 197 * @mp *a@ = source, assumed positive
d3409d5e 198 *
199 * Returns: Result, %$a R^{-1} \bmod m$%.
200 */
201
52e4b041 202#ifdef MPMONT_DISABLE
203
ef5f4810 204mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
52e4b041 205{
206 mp_div(0, &d, a, mm->m);
207 return (d);
208}
209
210#else
211
ef5f4810 212mp *mpmont_reduce(mpmont *mm, mp *d, mp *a)
d3409d5e 213{
f5f35081 214 size_t n = mm->n;
215
216 /* --- Check for serious Karatsuba reduction --- */
217
52cdaca9 218 if (n > MPK_THRESH * 3) {
f5f35081 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 }
d3409d5e 235
f5f35081 236 /* --- Otherwise do it the hard way --- */
d3409d5e 237
d3409d5e 238 else {
f5f35081 239 mpw *dv, *dvl;
240 mpw *mv, *mvl;
241 mpw mi;
242 size_t k = n;
243
244 /* --- Initial conditioning of the arguments --- */
245
d34decd2 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
f5f35081 252 dv = d->v; dvl = d->vl;
253 mv = mm->m->v; mvl = mm->m->vl;
d3409d5e 254
f5f35081 255 /* --- Let's go to work --- */
d3409d5e 256
f5f35081 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 }
d3409d5e 263 }
264
f5f35081 265 /* --- Wrap everything up --- */
d3409d5e 266
f5f35081 267 memmove(d->v, d->v + n, MPWS(MP_LEN(d) - n));
268 d->vl -= n;
032099d1 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 }
f5f35081 275 MP_SHRINK(d);
d3409d5e 276 return (d);
277}
278
52e4b041 279#endif
280
d3409d5e 281/* --- @mpmont_mul@ --- *
282 *
283 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
284 * @mp *d@ = destination
ef5f4810 285 * @mp *a, *b@ = sources, assumed positive
d3409d5e 286 *
287 * Returns: Result, %$a b R^{-1} \bmod m$%.
288 */
289
52e4b041 290#ifdef MPMONT_DISABLE
291
ef5f4810 292mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
52e4b041 293{
294 d = mp_mul(d, a, b);
295 mp_div(0, &d, d, mm->m);
296 return (d);
297}
298
299#else
300
ef5f4810 301mp *mpmont_mul(mpmont *mm, mp *d, mp *a, mp *b)
d3409d5e 302{
52cdaca9 303 if (mm->n > MPK_THRESH * 3) {
ef5f4810 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;
f5f35081 313 mpw mi;
ef5f4810 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);
d3409d5e 321
ef5f4810 322 a = MP_COPY(a);
323 b = MP_COPY(b);
d34decd2 324 MP_DEST(d, 2 * n + 1, a->f | b->f | MP_UNDEF);
ef5f4810 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;
f5f35081 335 mi = mm->mi->v[0];
ef5f4810 336 while (i < n && av < avl) {
337 mpw x = *av++;
f5f35081 338 mpw u = MPW((*dv + x * y) * mi);
ef5f4810 339 MPX_UMLAN(dv, dvl, bv, bvl, x);
340 MPX_UMLAN(dv, dvl, mv, mvl, u);
341 dv++;
342 i++;
343 }
d3409d5e 344
ef5f4810 345 /* --- Simpler Montgomery reduction phase --- */
d3409d5e 346
ef5f4810 347 while (i < n) {
f5f35081 348 mpw u = MPW(*dv * mi);
ef5f4810 349 MPX_UMLAN(dv, dvl, mv, mvl, u);
350 dv++;
351 i++;
352 }
d3409d5e 353
ef5f4810 354 /* --- Done --- */
d3409d5e 355
ef5f4810 356 memmove(d->v, dv, MPWS(dvl - dv));
357 d->vl -= dv - d->v;
032099d1 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);
ef5f4810 362 MP_SHRINK(d);
363 d->f = (a->f | b->f) & MP_BURN;
ef5f4810 364 MP_DROP(a);
365 MP_DROP(b);
d3409d5e 366 }
367
d3409d5e 368 return (d);
369}
370
52e4b041 371#endif
372
4640a0dd 373/*----- Exponentiation ----------------------------------------------------*/
374
17ad212e 375/* --- @mpmont_expr@ --- *
d3409d5e 376 *
377 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
ef5f4810 378 * @mp *d@ = fake destination
379 * @mp *a@ = base
380 * @mp *e@ = exponent
d3409d5e 381 *
4640a0dd 382 * Returns: Result, %$(a R^{-1})^e R \bmod m$%.
d3409d5e 383 */
384
c9d4c30b 385mp *mpmont_expr(mpmont *mm, mp *d, mp *a, mp *e)
386{
c9d4c30b 387 mp *x = MP_COPY(mm->r);
4640a0dd 388 mp *spare = (e->f & MP_BURN) ? MP_NEWSEC : MP_NEW;
c9d4c30b 389
390 MP_SHRINK(e);
391 if (MP_LEN(e) == 0)
4640a0dd 392 ;
393 else if (MP_LEN(e) < EXP_THRESH)
394 EXP_SIMPLE(x, a, e);
395 else
396 EXP_WINDOW(x, a, e);
f1140c41 397 mp_drop(d);
398 mp_drop(spare);
c9d4c30b 399 return (x);
400}
401
17ad212e 402/* --- @mpmont_exp@ --- *
403 *
404 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
ef5f4810 405 * @mp *d@ = fake destination
406 * @mp *a@ = base
407 * @mp *e@ = exponent
17ad212e 408 *
409 * Returns: Result, %$a^e \bmod m$%.
410 */
411
ef5f4810 412mp *mpmont_exp(mpmont *mm, mp *d, mp *a, mp *e)
17ad212e 413{
97490e68 414 e = MP_COPY(e);
4640a0dd 415 d = mpmont_mul(mm, d, a, mm->r2);
416 d = mpmont_expr(mm, d, d, e);
17ad212e 417 d = mpmont_reduce(mm, d, d);
97490e68 418 MP_DROP(e);
17ad212e 419 return (d);
d3409d5e 420}
421
422/*----- Test rig ----------------------------------------------------------*/
423
424#ifdef TEST_RIG
425
426static 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
f5f35081 438 if (mm.mi->v[0] != mi->v[0]) {
d3409d5e 439 fprintf(stderr, "\n*** bad mi: found %lu, expected %lu",
f5f35081 440 (unsigned long)mm.mi->v[0], (unsigned long)mi->v[0]);
d3409d5e 441 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
442 fputc('\n', stderr);
443 ok = 0;
444 }
445
032099d1 446 if (!MP_EQ(mm.r, r)) {
d3409d5e 447 fputs("\n*** bad r", stderr);
448 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
449 fputs("\nexpected ", stderr); mp_writefile(r, stderr, 10);
17ad212e 450 fputs("\n found ", stderr); mp_writefile(mm.r, stderr, 10);
d3409d5e 451 fputc('\n', stderr);
452 ok = 0;
453 }
454
032099d1 455 if (!MP_EQ(mm.r2, r2)) {
d3409d5e 456 fputs("\n*** bad r2", stderr);
457 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
458 fputs("\nexpected ", stderr); mp_writefile(r2, stderr, 10);
17ad212e 459 fputs("\n found ", stderr); mp_writefile(mm.r2, stderr, 10);
d3409d5e 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);
ef5f4810 469 assert(mparena_count(MPARENA_GLOBAL) == 0);
d3409d5e 470 return (ok);
471}
472
473static 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;
d3409d5e 479 int ok = 1;
480
481 mpmont mm;
482 mpmont_create(&mm, m);
483
484 {
79a34029 485 mp *qr = mp_mul(MP_NEW, a, b);
486 mp_div(0, &qr, qr, m);
487
032099d1 488 if (!MP_EQ(qr, r)) {
79a34029 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 {
d3409d5e 503 mp *ar = mpmont_mul(&mm, MP_NEW, a, mm.r2);
504 mp *br = mpmont_mul(&mm, MP_NEW, b, mm.r2);
79a34029 505 mp *mr = mpmont_mul(&mm, MP_NEW, ar, br);
d3409d5e 506 mr = mpmont_reduce(&mm, mr, mr);
032099d1 507 if (!MP_EQ(mr, r)) {
79a34029 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 }
d3409d5e 517 MP_DROP(ar); MP_DROP(br);
79a34029 518 mp_drop(mr);
d3409d5e 519 }
520
d3409d5e 521
522 MP_DROP(m);
523 MP_DROP(a);
524 MP_DROP(b);
525 MP_DROP(r);
d3409d5e 526 mpmont_destroy(&mm);
ef5f4810 527 assert(mparena_count(MPARENA_GLOBAL) == 0);
d3409d5e 528 return ok;
529}
530
531static 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
ef5f4810 543 mr = mpmont_exp(&mm, MP_NEW, a, b);
d3409d5e 544
032099d1 545 if (!MP_EQ(mr, r)) {
d3409d5e 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);
ef5f4810 562 assert(mparena_count(MPARENA_GLOBAL) == 0);
d3409d5e 563 return ok;
564}
565
566
567static test_chunk tests[] = {
ef5f4810 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 } },
d3409d5e 571 { 0, 0, { 0 } },
572};
573
574int 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 -------------------------------------------------*/