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