Integrate testing for MPX routines.
[u/mdw/catacomb] / mpmont.c
CommitLineData
d3409d5e 1/* -*-c-*-
2 *
17ad212e 3 * $Id: mpmont.c,v 1.2 1999/11/19 13:17:26 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 $
17ad212e 33 * Revision 1.2 1999/11/19 13:17:26 mdw
34 * Add extra interface to exponentiation which returns a Montgomerized
35 * result.
36 *
d3409d5e 37 * Revision 1.1 1999/11/17 18:02:16 mdw
38 * New multiprecision integer arithmetic suite.
39 *
40 */
41
42/*----- Header files ------------------------------------------------------*/
43
44#include "mp.h"
45#include "mpmont.h"
46
47/*----- Main code ---------------------------------------------------------*/
48
49/* --- @mpmont_create@ --- *
50 *
51 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
52 * @mp *m@ = modulus to use
53 *
54 * Returns: ---
55 *
56 * Use: Initializes a Montgomery reduction context ready for use.
57 */
58
59void mpmont_create(mpmont *mm, mp *m)
60{
61 /* --- Take a copy of the modulus --- */
62
63 mp_shrink(m);
64 mm->m = MP_COPY(m);
65
66 /* --- Find the magic value @mi@ --- *
67 *
68 * This is a slightly grungy way of solving the problem, but it does work.
69 */
70
71 {
72 mpw av[2] = { 0, 1 };
73 mp a, b;
74 mp *i;
75 mpw mi;
76
77 mp_build(&a, av, av + 2);
78 mp_build(&b, m->v, m->v + 1);
79 mp_gcd(0, 0, &i, &a, &b);
80 mi = i->v[0];
81 if (!(i->f & MP_NEG))
82 mi = MPW(-mi);
83 mm->mi = mi;
84 MP_DROP(i);
85 }
86
87 /* --- Discover the values %$R \bmod m$% and %$R^2 \bmod m$% --- */
88
89 {
90 size_t l = MP_LEN(m);
91 mp *r = mp_create(l + 1);
92
93 mm->shift = l * MPW_BITS;
94 MPX_ZERO(r->v, r->vl - 1);
95 r->vl[-1] = 1;
96 mm->r = mm->r2 = MP_NEW;
97 mp_div(0, &mm->r, r, m);
98 r = mp_sqr(r, mm->r);
99 mp_div(0, &mm->r2, r, m);
100 MP_DROP(r);
101 }
102}
103
104/* --- @mpmont_destroy@ --- *
105 *
106 * Arguments: @mpmont *mm@ = pointer to a Montgomery reduction context
107 *
108 * Returns: ---
109 *
110 * Use: Disposes of a context when it's no longer of any use to
111 * anyone.
112 */
113
114void mpmont_destroy(mpmont *mm)
115{
116 MP_DROP(mm->m);
117 MP_DROP(mm->r);
118 MP_DROP(mm->r2);
119}
120
121/* --- @mpmont_reduce@ --- *
122 *
123 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
124 * @mp *d@ = destination
125 * @const mp *a@ = source, assumed positive
126 *
127 * Returns: Result, %$a R^{-1} \bmod m$%.
128 */
129
130mp *mpmont_reduce(mpmont *mm, mp *d, const mp *a)
131{
132 mpw *dv, *dvl;
133 const mpw *mv, *mvl;
134 size_t n;
135
136 /* --- Initial conditioning of the arguments --- */
137
138 n = MP_LEN(mm->m);
139
140 if (d == a)
141 MP_MODIFY(d, 2 * n);
142 else {
143 MP_MODIFY(d, 2 * n);
144 memcpy(d->v, a->v, MPWS(MP_LEN(a)));
145 memset(d->v + MP_LEN(a), 0, MPWS(MP_LEN(d) - MP_LEN(a)));
146 }
147
148 dv = d->v; dvl = d->vl;
149 mv = mm->m->v; mvl = mm->m->vl;
150
151 /* --- Let's go to work --- */
152
153 while (n--) {
154 mpw u = MPW(*dv * mm->mi);
155 MPX_UMLAN(dv, dvl, mv, mvl, u);
156 dv++;
157 }
158
159 /* --- Done --- */
160
161 memmove(d->v, dv, MPWS(dvl - dv));
162 d->vl -= dv - d->v;
163 MP_SHRINK(d);
164 d->f = a->f & MP_BURN;
17ad212e 165 if (MP_CMP(d, >=, mm->m))
166 d = mp_sub(d, d, mm->m);
d3409d5e 167 return (d);
168}
169
170/* --- @mpmont_mul@ --- *
171 *
172 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
173 * @mp *d@ = destination
174 * @const mp *a, *b@ = sources, assumed positive
175 *
176 * Returns: Result, %$a b R^{-1} \bmod m$%.
177 */
178
179mp *mpmont_mul(mpmont *mm, mp *d, const mp *a, const mp *b)
180{
181 mpw *dv, *dvl;
182 const mpw *av, *avl;
183 const mpw *bv, *bvl;
184 const mpw *mv, *mvl;
185 mpw y;
186 size_t n, i;
187
188 /* --- Initial conditioning of the arguments --- */
189
190 if (MP_LEN(a) > MP_LEN(b)) {
191 const mp *t = a; a = b; b = t;
192 }
193 n = MP_LEN(mm->m);
194
195 MP_MODIFY(d, 2 * n + 1);
196 dv = d->v; dvl = d->vl;
197 MPX_ZERO(dv, dvl);
198 av = a->v; avl = a->vl;
199 bv = b->v; bvl = b->vl;
200 mv = mm->m->v; mvl = mm->m->vl;
201 y = *bv;
202
203 /* --- Montgomery multiplication phase --- */
204
205 i = 0;
206 while (i < n && av < avl) {
207 mpw x = *av++;
208 mpw u = MPW((*dv + x * y) * mm->mi);
209 MPX_UMLAN(dv, dvl, bv, bvl, x);
210 MPX_UMLAN(dv, dvl, mv, mvl, u);
211 dv++;
212 i++;
213 }
214
215 /* --- Simpler Montgomery reduction phase --- */
216
217 while (i < n) {
218 mpw u = MPW(*dv * mm->mi);
219 MPX_UMLAN(dv, dvl, mv, mvl, u);
220 dv++;
221 i++;
222 }
223
224 /* --- Done --- */
225
226 memmove(d->v, dv, MPWS(dvl - dv));
227 d->vl -= dv - d->v;
228 MP_SHRINK(d);
229 d->f = (a->f | b->f) & MP_BURN;
17ad212e 230 if (MP_CMP(d, >=, mm->m))
231 d = mp_sub(d, d, mm->m);
d3409d5e 232 return (d);
233}
234
17ad212e 235/* --- @mpmont_expr@ --- *
d3409d5e 236 *
237 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
238 * @const mp *a@ = base
239 * @const mp *e@ = exponent
240 *
17ad212e 241 * Returns: Result, %$a^e R \bmod m$%.
d3409d5e 242 */
243
17ad212e 244mp *mpmont_expr(mpmont *mm, const mp *a, const mp *e)
d3409d5e 245{
246 mpscan sc;
247 mp *ar = mpmont_mul(mm, MP_NEW, a, mm->r2);
248 mp *d = MP_COPY(mm->r);
17ad212e 249 mp *spare = MP_NEW;
d3409d5e 250
251 mp_scan(&sc, e);
252
253 if (MP_STEP(&sc)) {
17ad212e 254 size_t sq = 0;
d3409d5e 255 for (;;) {
256 mp *dd;
257 if (MP_BIT(&sc)) {
17ad212e 258 while (sq) {
259 dd = mpmont_mul(mm, spare, ar, ar);
260 spare = ar; ar = dd;
261 sq--;
262 }
263 dd = mpmont_mul(mm, spare, d, ar);
264 spare = d; d = dd;
d3409d5e 265 }
17ad212e 266 sq++;
d3409d5e 267 if (!MP_STEP(&sc))
268 break;
d3409d5e 269 }
270 }
271 MP_DROP(ar);
17ad212e 272 if (spare != MP_NEW)
273 MP_DROP(spare);
274 return (d);
275}
276
277/* --- @mpmont_exp@ --- *
278 *
279 * Arguments: @mpmont *mm@ = pointer to Montgomery reduction context
280 * @const mp *a@ = base
281 * @const mp *e@ = exponent
282 *
283 * Returns: Result, %$a^e \bmod m$%.
284 */
285
286mp *mpmont_exp(mpmont *mm, const mp *a, const mp *e)
287{
288 mp *d = mpmont_expr(mm, a, e);
289 d = mpmont_reduce(mm, d, d);
290 return (d);
d3409d5e 291}
292
293/*----- Test rig ----------------------------------------------------------*/
294
295#ifdef TEST_RIG
296
297static int tcreate(dstr *v)
298{
299 mp *m = *(mp **)v[0].buf;
300 mp *mi = *(mp **)v[1].buf;
301 mp *r = *(mp **)v[2].buf;
302 mp *r2 = *(mp **)v[3].buf;
303
304 mpmont mm;
305 int ok = 1;
306
307 mpmont_create(&mm, m);
308
309 if (mm.mi != mi->v[0]) {
310 fprintf(stderr, "\n*** bad mi: found %lu, expected %lu",
311 (unsigned long)mm.mi, (unsigned long)mi->v[0]);
312 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
313 fputc('\n', stderr);
314 ok = 0;
315 }
316
317 if (MP_CMP(mm.r, !=, r)) {
318 fputs("\n*** bad r", stderr);
319 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
320 fputs("\nexpected ", stderr); mp_writefile(r, stderr, 10);
17ad212e 321 fputs("\n found ", stderr); mp_writefile(mm.r, stderr, 10);
d3409d5e 322 fputc('\n', stderr);
323 ok = 0;
324 }
325
326 if (MP_CMP(mm.r2, !=, r2)) {
327 fputs("\n*** bad r2", stderr);
328 fputs("\nm = ", stderr); mp_writefile(m, stderr, 10);
329 fputs("\nexpected ", stderr); mp_writefile(r2, stderr, 10);
17ad212e 330 fputs("\n found ", stderr); mp_writefile(mm.r2, stderr, 10);
d3409d5e 331 fputc('\n', stderr);
332 ok = 0;
333 }
334
335 MP_DROP(m);
336 MP_DROP(mi);
337 MP_DROP(r);
338 MP_DROP(r2);
339 mpmont_destroy(&mm);
340 return (ok);
341}
342
343static int tmul(dstr *v)
344{
345 mp *m = *(mp **)v[0].buf;
346 mp *a = *(mp **)v[1].buf;
347 mp *b = *(mp **)v[2].buf;
348 mp *r = *(mp **)v[3].buf;
349 mp *mr, *qr;
350 int ok = 1;
351
352 mpmont mm;
353 mpmont_create(&mm, m);
354
355 {
356 mp *ar = mpmont_mul(&mm, MP_NEW, a, mm.r2);
357 mp *br = mpmont_mul(&mm, MP_NEW, b, mm.r2);
358 mr = mpmont_mul(&mm, MP_NEW, ar, br);
359 mr = mpmont_reduce(&mm, mr, mr);
360 MP_DROP(ar); MP_DROP(br);
361 }
362
363 qr = mp_mul(MP_NEW, a, b);
364 mp_div(0, &qr, qr, m);
365
366 if (MP_CMP(qr, !=, r)) {
367 fputs("\n*** classical modmul failed", stderr);
368 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
369 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
370 fputs("\n b = ", stderr); mp_writefile(b, stderr, 10);
371 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
372 fputs("\nqr = ", stderr); mp_writefile(qr, stderr, 10);
373 fputc('\n', stderr);
374 ok = 0;
375 }
376
377 if (MP_CMP(mr, !=, r)) {
378 fputs("\n*** montgomery modmul failed", stderr);
379 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
380 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
381 fputs("\n b = ", stderr); mp_writefile(b, stderr, 10);
382 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
383 fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
384 fputc('\n', stderr);
385 ok = 0;
386 }
387
388 MP_DROP(m);
389 MP_DROP(a);
390 MP_DROP(b);
391 MP_DROP(r);
392 MP_DROP(mr);
393 MP_DROP(qr);
394 mpmont_destroy(&mm);
395 return ok;
396}
397
398static int texp(dstr *v)
399{
400 mp *m = *(mp **)v[0].buf;
401 mp *a = *(mp **)v[1].buf;
402 mp *b = *(mp **)v[2].buf;
403 mp *r = *(mp **)v[3].buf;
404 mp *mr;
405 int ok = 1;
406
407 mpmont mm;
408 mpmont_create(&mm, m);
409
410 mr = mpmont_exp(&mm, a, b);
411
412 if (MP_CMP(mr, !=, r)) {
413 fputs("\n*** montgomery modexp failed", stderr);
414 fputs("\n m = ", stderr); mp_writefile(m, stderr, 10);
415 fputs("\n a = ", stderr); mp_writefile(a, stderr, 10);
416 fputs("\n e = ", stderr); mp_writefile(b, stderr, 10);
417 fputs("\n r = ", stderr); mp_writefile(r, stderr, 10);
418 fputs("\nmr = ", stderr); mp_writefile(mr, stderr, 10);
419 fputc('\n', stderr);
420 ok = 0;
421 }
422
423 MP_DROP(m);
424 MP_DROP(a);
425 MP_DROP(b);
426 MP_DROP(r);
427 MP_DROP(mr);
428 mpmont_destroy(&mm);
429 return ok;
430}
431
432
433static test_chunk tests[] = {
434 { "create", tcreate, { &type_mp, &type_mp, &type_mp, &type_mp } },
435 { "mul", tmul, { &type_mp, &type_mp, &type_mp, &type_mp } },
436 { "exp", texp, { &type_mp, &type_mp, &type_mp, &type_mp } },
437 { 0, 0, { 0 } },
438};
439
440int main(int argc, char *argv[])
441{
442 sub_init();
443 test_run(argc, argv, tests, SRCDIR "/tests/mpmont");
444 return (0);
445}
446
447#endif
448
449/*----- That's all, folks -------------------------------------------------*/