]>
Commit | Line | Data |
---|---|---|
b1ab9ed8 A |
1 | /* schoof.c |
2 | ||
3 | Elliptic curve order calculator, for | |
4 | ||
5 | y^2 = x^3 + a x + b (mod p) | |
6 | ||
7 | Compile with: | |
8 | ||
9 | % cc -O schoof.c giants.c tools.c ellproj.c -lm -o schoof | |
10 | ||
11 | and run, entering p and the a,b parameters. | |
12 | Eventually the curve order o is reported, together with | |
13 | the twist order o'. The sum of these two orders is always | |
14 | 2p + 2. A final check is performed to verify that a | |
15 | random point (x,y,z) enjoys the annihilation | |
16 | o * (x,y,z) = point-at-infinity. This is not absolutely | |
17 | definitive, rather it is a necessary condition on the oder o | |
18 | (i.e. it is a sanity check of sorts). | |
19 | ||
20 | * Change history: | |
21 | ||
22 | 18 Nov 99 REC fixed M. Scott bug (MAX_DIGS bumped by 2) | |
23 | 5 Jul 99 REC Installed improved (base-4) power ladder | |
24 | 5 Jul 99 REC Made use of binary segmentation square (per se) | |
25 | 5 Jul 99 REC Improved memory usage | |
26 | 2 May 99 REC Added initial primality check | |
27 | 30 Apr 99 REC Added actual order-annihilation test | |
28 | 29 Apr 99 REC Improvements due to A. Powell | |
29 | 2 Feb 99 REC Added explicit CRT result | |
30 | 12 Jan 99 REC Removed (hopefully) last of memory crashes | |
31 | 20 Jan 98 REC Creation | |
32 | ||
33 | * c. 1998 Perfectly Scientific, Inc. | |
34 | * All Rights Reserved. | |
35 | * | |
36 | * | |
37 | *************************************************************/ | |
38 | ||
39 | #include <stdio.h> | |
40 | #include<assert.h> | |
41 | #include <math.h> | |
42 | #include <stdlib.h> | |
43 | #include "giants.h" | |
44 | #include "tools.h" | |
45 | #include "ellproj.h" | |
46 | ||
47 | #define P_BREAK 32 | |
48 | ||
49 | #ifdef _WIN32 | |
50 | #include <string.h> | |
51 | #define bzero(D, n) memset(D, 0x00, n) | |
52 | #define bcopy(S, D, n) memcpy(D, S, n) | |
53 | #endif | |
54 | ||
55 | #define Q_MAX 256 /* Bits in largest primes handled. */ | |
56 | #define L_CEIL 100 /* Bound on Schoof L values (not all needed in general). */ | |
57 | ||
58 | ||
59 | typedef struct | |
60 | { | |
61 | int deg; | |
62 | giant *coe; | |
63 | } polystruct; | |
64 | typedef polystruct *poly; | |
65 | ||
66 | extern int *pr; | |
67 | ||
68 | static int Q, L_MAX; | |
69 | static int MAX_DIGS, MAX_COEFFS; | |
70 | ||
71 | static giant *mcand, coe, tmp, err, aux, aux2, globx, globy, t1, t2, | |
72 | t3, t4, t5; | |
73 | static poly qscratch, rscratch, sscratch, tscratch, pbuff, | |
74 | pbuff2, precip, cubic, powx, powy, kxn, kyn, kxd, kyd, | |
75 | txn, txd, tyn, tyd, txn1, txd1, tyn1, tyd1, | |
76 | nx, dx, ny, dy, mn, md, tmp1, tmp2, tmp3, tmp4; | |
77 | static poly s[L_CEIL+2], smonic; | |
78 | static giant p, a, b; | |
79 | static int L; | |
80 | ||
81 | void quickmodg(giant g, giant x) | |
82 | { int sgn = x->sign; | |
83 | ||
84 | if(sgn == 0) return; | |
85 | if(sgn > 0) { | |
86 | if(gcompg(x, g) >= 0) subg(g, x); | |
87 | return; | |
88 | } | |
89 | addg(g,x); | |
90 | return; | |
91 | } | |
92 | ||
93 | int | |
94 | log_2(int n) { | |
95 | int c = 1, d = -1; | |
96 | while(c <= n) { | |
97 | c <<= 1; | |
98 | ++d; | |
99 | } | |
100 | return(d); | |
101 | } | |
102 | ||
103 | void | |
104 | justifyp(poly x) { | |
105 | int j; | |
106 | for(j = x->deg; j >= 0; j--) { | |
107 | if(!is0(x->coe[j])) break; | |
108 | } | |
109 | x->deg = (j>0)? j : 0; | |
110 | } | |
111 | ||
112 | void | |
113 | polyrem(poly x) { | |
114 | int j; | |
115 | for(j=0; j <= x->deg; j++) { | |
116 | modg(p, x->coe[j]); | |
117 | } | |
118 | justifyp(x); | |
119 | } | |
120 | ||
121 | ||
122 | giant * | |
123 | newa(int n) { | |
124 | giant *p = (giant *)malloc(n*sizeof(giant)); | |
125 | int j; | |
126 | for(j=0; j<n; j++) { | |
127 | p[j] = newgiant(MAX_DIGS); | |
128 | } | |
129 | return(p); | |
130 | } | |
131 | ||
132 | poly | |
133 | newpoly(int coeffs) { | |
134 | poly pol; | |
135 | pol = (poly) malloc(sizeof(polystruct)); | |
136 | pol->coe = (giant *)newa(coeffs); | |
137 | return(pol); | |
138 | } | |
139 | ||
140 | void | |
141 | init_all() { | |
142 | int j; | |
143 | ||
144 | j = (2*Q + log_2(MAX_COEFFS) + 32 + 15)/16; | |
145 | j = j * MAX_COEFFS; | |
146 | globx = newgiant(j); | |
147 | globy = newgiant(j); | |
148 | s[0] = newpoly(MAX_COEFFS); | |
149 | ||
150 | for(j=1; j<=L_MAX+1; j++) { | |
151 | s[j] = newpoly(j*(j+1)); | |
152 | } | |
153 | smonic = newpoly(MAX_COEFFS); | |
154 | powx = newpoly(MAX_COEFFS); | |
155 | powy = newpoly(MAX_COEFFS); | |
156 | kxn = newpoly(MAX_COEFFS); | |
157 | kxd = newpoly(MAX_COEFFS); | |
158 | kyn = newpoly(MAX_COEFFS); | |
159 | kyd = newpoly(MAX_COEFFS); | |
160 | txn = newpoly(MAX_COEFFS); | |
161 | txd = newpoly(MAX_COEFFS); | |
162 | tyn = newpoly(MAX_COEFFS); | |
163 | tyd = newpoly(MAX_COEFFS); | |
164 | txn1 = newpoly(MAX_COEFFS); | |
165 | txd1 = newpoly(MAX_COEFFS); | |
166 | tyn1 = newpoly(MAX_COEFFS); | |
167 | tyd1 = newpoly(MAX_COEFFS); | |
168 | nx = newpoly(MAX_COEFFS); | |
169 | ny = newpoly(MAX_COEFFS); | |
170 | dx = newpoly(MAX_COEFFS); | |
171 | dy = newpoly(MAX_COEFFS); | |
172 | mn = newpoly(MAX_COEFFS); | |
173 | md = newpoly(MAX_COEFFS); | |
174 | tmp1 = newpoly(MAX_COEFFS); | |
175 | tmp2 = newpoly(MAX_COEFFS); | |
176 | tmp3 = newpoly(MAX_COEFFS); | |
177 | tmp4 = newpoly(MAX_COEFFS); | |
178 | mcand = (giant *)newa(MAX_COEFFS); | |
179 | /* The next three need extra space for remaindering routines. */ | |
180 | qscratch = newpoly(2*MAX_COEFFS); | |
181 | rscratch = newpoly(2*MAX_COEFFS); | |
182 | pbuff = newpoly(2*MAX_COEFFS); | |
183 | pbuff2 = newpoly(MAX_COEFFS); | |
184 | sscratch = newpoly(MAX_COEFFS); | |
185 | tscratch = newpoly(MAX_COEFFS); | |
186 | tmp = newgiant(MAX_DIGS); | |
187 | err = newgiant(MAX_DIGS); | |
188 | coe = newgiant(MAX_DIGS); | |
189 | aux = newgiant(MAX_DIGS); | |
190 | aux2 = newgiant(MAX_DIGS); | |
191 | t3 = newgiant(MAX_DIGS); | |
192 | t4 = newgiant(MAX_DIGS); | |
193 | t5 = newgiant(MAX_DIGS); | |
194 | cubic = newpoly(4); | |
195 | precip = newpoly(MAX_COEFFS); | |
196 | } | |
197 | ||
198 | void | |
199 | atoa(giant *a, giant *b, int n) { | |
200 | int j; | |
201 | for(j=0; j<n; j++) gtog(a[j], b[j]); | |
202 | } | |
203 | ||
204 | void | |
205 | ptop(poly x, poly y) | |
206 | /* y := x. */ | |
207 | { | |
208 | y->deg = x->deg; | |
209 | atoa(x->coe, y->coe, y->deg+1); | |
210 | } | |
211 | ||
212 | void | |
213 | negp(poly y) | |
214 | /* y := -y. */ | |
215 | { int j; | |
216 | for(j=0; j <= y->deg; j++) { | |
217 | negg(y->coe[j]); | |
218 | quickmodg(p, y->coe[j]); | |
219 | } | |
220 | } | |
221 | ||
222 | int | |
223 | iszer(giant a) { | |
224 | ||
225 | if(a->sign == 0) return(1); | |
226 | return(0); | |
227 | ||
228 | } | |
229 | ||
230 | int | |
231 | iszerop(poly x) { | |
232 | if(x->deg == 0 && iszer(x->coe[0])) return 1; | |
233 | return 0; | |
234 | } | |
235 | ||
236 | int | |
237 | is0(giant a) { | |
238 | return(iszer(a)); | |
239 | } | |
240 | ||
241 | int | |
242 | is1(giant a) { | |
243 | return(isone(a)); | |
244 | } | |
245 | ||
246 | ||
247 | void | |
248 | addp(poly x, poly y) | |
249 | /* y += x. */ | |
250 | { | |
251 | int d = x->deg, j; | |
252 | ||
253 | if(y->deg > d) d = y->deg; | |
254 | for(j = 0; j <= d; j++) { | |
255 | if((j <= x->deg) && (j <= y->deg)) { | |
256 | addg(x->coe[j], y->coe[j]); | |
257 | quickmodg(p, y->coe[j]); | |
258 | continue; | |
259 | } | |
260 | if((j <= x->deg) && (j > y->deg)) { | |
261 | gtog(x->coe[j], y->coe[j]); | |
262 | quickmodg(p, y->coe[j]); | |
263 | continue; | |
264 | } | |
265 | } | |
266 | y->deg = d; | |
267 | justifyp(y); | |
268 | } | |
269 | ||
270 | void | |
271 | subp(poly x, poly y) | |
272 | /* y -= x. */ | |
273 | { | |
274 | int d = x->deg, j; | |
275 | ||
276 | if(y->deg > d) d = y->deg; | |
277 | for(j = 0; j <= d; j++) { | |
278 | if((j <= x->deg) && (j <= y->deg)) { | |
279 | subg(x->coe[j], y->coe[j]); | |
280 | quickmodg(p, y->coe[j]); | |
281 | continue; | |
282 | } | |
283 | if((j <= x->deg) && (j > y->deg)) { | |
284 | gtog(x->coe[j], y->coe[j]); | |
285 | negg(y->coe[j]); | |
286 | quickmodg(p, y->coe[j]); | |
287 | continue; | |
288 | } | |
289 | } | |
290 | y->deg = d; | |
291 | justifyp(y); | |
292 | } | |
293 | ||
294 | ||
295 | void | |
296 | grammarmulp(poly a, poly b) | |
297 | /* b *= a. */ | |
298 | { | |
299 | int dega = a->deg, degb = b->deg, deg = dega + degb; | |
300 | register int d, kk, first, diffa; | |
301 | ||
302 | for(d=deg; d>=0; d--) { | |
303 | diffa = d-dega; | |
304 | itog(0, coe); | |
305 | for(kk=0; kk<=d; kk++) { | |
306 | if((kk>degb)||(kk<diffa)) continue; | |
307 | gtog(b->coe[kk], tmp); | |
308 | mulg(a->coe[d-kk], tmp); | |
309 | modg(p, tmp); | |
310 | addg(tmp, coe); | |
311 | quickmodg(p, coe); | |
312 | } | |
313 | gtog(coe, mcand[d]); | |
314 | } | |
315 | atoa(mcand, b->coe, deg+1); | |
316 | b->deg = deg; | |
317 | justifyp(b); | |
318 | } | |
319 | ||
320 | void | |
321 | atop(giant *a, poly x, int deg) | |
322 | /* Copy array to poly, with monic option. */ | |
323 | { | |
324 | int adeg = abs(deg); | |
325 | x->deg = adeg; | |
326 | atoa(a, x->coe, adeg); | |
327 | if(deg < 0) { | |
328 | itog(1, x->coe[adeg]); | |
329 | } else { | |
330 | gtog(a[adeg], x->coe[adeg]); | |
331 | } | |
332 | } | |
333 | ||
334 | void | |
335 | just(giant g) { | |
336 | while((g->n[g->sign-1] == 0) && (g->sign > 0)) --g->sign; | |
337 | } | |
338 | ||
339 | void | |
340 | unstuff_partial(giant g, poly y, int words){ | |
341 | int j; | |
342 | for(j=0; j < y->deg; j++) { | |
343 | bcopy((g->n) + j*words, y->coe[j]->n, words*sizeof(short)); | |
344 | y->coe[j]->sign = words; | |
345 | just(y->coe[j]); | |
346 | } | |
347 | } | |
348 | ||
349 | void | |
350 | stuff(poly x, giant g, int words) { | |
351 | int deg = x->deg, j, coedigs; | |
352 | ||
353 | g->sign = words*(1 + deg); | |
354 | for(j=0; j <= deg; j++) { | |
355 | coedigs = (x->coe[j])->sign; | |
356 | bcopy(x->coe[j]->n, (g->n) + j*words, coedigs*sizeof(short)); | |
357 | bzero((g->n) + (j*words+coedigs), | |
358 | sizeof(short)*(words-coedigs)); | |
359 | } | |
360 | just(g); | |
361 | } | |
362 | ||
363 | int maxwords = 0; | |
364 | void | |
365 | ||
366 | binarysegmul(poly x, poly y) { | |
367 | int bits = bitlen(p), xwords, ywords, words; | |
368 | ||
369 | xwords = (2*bits + log_2(x->deg+1) + 32 + 15)/16; | |
370 | ywords = (2*bits + log_2(y->deg+1) + 32 + 15)/16; | |
371 | if(xwords > ywords) words = xwords; else words = ywords; | |
372 | stuff(x, globx, words); | |
373 | stuff(y, globy, words); | |
374 | mulg(globx, globy); | |
375 | gtog(y->coe[y->deg], globx); /* Save high coeff. */ | |
376 | y->deg += x->deg; | |
377 | gtog(globx, y->coe[y->deg]); /* Move high coeff. */ | |
378 | unstuff_partial(globy, y, words); | |
379 | mulg(x->coe[x->deg], y->coe[y->deg]); /* resolve high coeff. */ | |
380 | justifyp(y); | |
381 | } | |
382 | ||
383 | binarysegsquare(poly y) { | |
384 | int bits = bitlen(p), words; | |
385 | words = (2*bits + log_2(y->deg+1) + 32 + 15)/16; | |
386 | stuff(y, globy, words); | |
387 | squareg(globy); | |
388 | gtog(y->coe[y->deg], globx); /* Save high coeff. */ | |
389 | y->deg += y->deg; | |
390 | gtog(globx, y->coe[y->deg]); /* Move high coeff. */ | |
391 | unstuff_partial(globy, y, words); | |
392 | mulg(y->coe[y->deg], y->coe[y->deg]); /* resolve high coeff. */ | |
393 | justifyp(y); | |
394 | } | |
395 | ||
396 | void | |
397 | assess(poly x, poly y){ | |
398 | int max = 0, j; | |
399 | for(j=0; j <= x->deg; j++) { | |
400 | if(bitlen(x->coe[j]) > max) max = bitlen(x->coe[j]); | |
401 | } | |
402 | max = 0; | |
403 | for(j=0; j <= y->deg; j++) { | |
404 | if(bitlen(y->coe[j]) > max) max = bitlen(y->coe[j]); | |
405 | } | |
406 | } | |
407 | ||
408 | int | |
409 | pcompp(poly x, poly y) { | |
410 | int j; | |
411 | if(x->deg != y->deg) return 1; | |
412 | for(j=0; j <= x->deg; j++) { | |
413 | if(gcompg(x->coe[j], y->coe[j])) return 1; | |
414 | } | |
415 | return 0; | |
416 | } | |
417 | ||
418 | /* | |
419 | int max_deg = 0; | |
420 | */ | |
421 | ||
422 | void | |
423 | mulp(poly x, poly y) | |
424 | /* y *= x. */ | |
425 | { | |
426 | int n, degx = x->deg, degy = y->deg; | |
427 | ||
428 | /* | |
429 | if(degx > max_deg) { | |
430 | max_deg = degx; printf("xdeg: %d\n", degx); | |
431 | } | |
432 | ||
433 | if(degy > max_deg) { | |
434 | max_deg = degy; printf("ydeg: %d\n", degy); | |
435 | } | |
436 | */ | |
437 | if((degx < P_BREAK) || (degy < P_BREAK)) { | |
438 | grammarmulp(x,y); | |
439 | justifyp(y); | |
440 | return; | |
441 | } | |
442 | if(x==y) binarysegsquare(y); | |
443 | else binarysegmul(x, y); | |
444 | } | |
445 | ||
446 | void | |
447 | revp(poly x) | |
448 | /* Reverse the coefficients of x. */ | |
449 | { int j, deg = x->deg; | |
450 | ||
451 | for(j=0; j <= deg/2; j++) { | |
452 | gtog(x->coe[deg-j], tmp); | |
453 | gtog(x->coe[j], x->coe[deg-j]); | |
454 | gtog(tmp, x->coe[j]); | |
455 | } | |
456 | justifyp(x); | |
457 | } | |
458 | ||
459 | void | |
460 | recipp(poly f, int deg) | |
461 | /* f := 1/f, via newton method. */ | |
462 | { | |
463 | int lim = deg + 1, prec = 1; | |
464 | ||
465 | sscratch->deg = 0; itog(1, sscratch->coe[0]); | |
466 | itog(1, aux); | |
467 | while(prec < lim) { | |
468 | prec <<= 1; | |
469 | if(prec > lim) prec = lim; | |
470 | f->deg = prec-1; | |
471 | ptop(sscratch, tscratch); | |
472 | mulp(f, tscratch); | |
473 | tscratch->deg = prec-1; | |
474 | polyrem(tscratch); | |
475 | subg(aux, tscratch->coe[0]); | |
476 | quickmodg(p, tscratch->coe[0]); | |
477 | mulp(sscratch, tscratch); | |
478 | tscratch->deg = prec-1; | |
479 | polyrem(tscratch); | |
480 | subp(tscratch, sscratch); | |
481 | sscratch->deg = prec-1; | |
482 | polyrem(sscratch); | |
483 | } | |
484 | justifyp(sscratch); | |
485 | ptop(sscratch, f); | |
486 | } | |
487 | ||
488 | int | |
489 | left_justifyp(poly x, int start) | |
490 | /* Left-justify the polynomial, checking from position "start." */ | |
491 | { | |
492 | int j, shift = 0; | |
493 | ||
494 | for(j = start; j <= x->deg; j++) { | |
495 | if(!is0(x->coe[j])) { | |
496 | shift = start; | |
497 | break; | |
498 | } | |
499 | } | |
500 | x->deg -= shift; | |
501 | for(j=0; j<= x->deg; j++) { | |
502 | gtog(x->coe[j+shift], x->coe[j]); | |
503 | } | |
504 | return(shift); | |
505 | } | |
506 | ||
507 | void | |
508 | remp(poly x, poly y, int mode) | |
509 | /* y := x (mod y). | |
510 | mode = 0 is normal operation, | |
511 | = 1 jams a fixed reciprocal, | |
512 | = 2 uses the fixed reciprocal. | |
513 | */ | |
514 | { | |
515 | int degx = x->deg, degy = y->deg, d, shift; | |
516 | ||
517 | if(degy == 0) { | |
518 | y->deg = 0; | |
519 | itog(0, y->coe[0]); | |
520 | return; | |
521 | } | |
522 | d = degx - degy; | |
523 | if(d < 0) { | |
524 | ptop(x, y); | |
525 | return; | |
526 | } | |
527 | revp(x); revp(y); | |
528 | ptop(y, rscratch); | |
529 | switch(mode) { | |
530 | case 0: recipp(rscratch, d); | |
531 | break; | |
532 | case 1: recipp(rscratch, degy); /* degy -1. */ | |
533 | ptop(rscratch, precip); | |
534 | rscratch->deg = d; justifyp(rscratch); | |
535 | break; | |
536 | case 2: ptop(precip, rscratch); | |
537 | rscratch->deg = d; justifyp(rscratch); | |
538 | break; | |
539 | } | |
540 | /* Next, a limited-precision multiply. */ | |
541 | if(d < degx) { x->deg = d; justifyp(x);} | |
542 | mulp(x, rscratch); | |
543 | rscratch->deg = d; | |
544 | polyrem(rscratch); | |
545 | justifyp(rscratch); | |
546 | x->deg = degx; justifyp(x); | |
547 | mulp(rscratch, y); | |
548 | subp(x, y); | |
549 | negp(y); polyrem(y); | |
550 | shift = left_justifyp(y, d+1); | |
551 | for(d = y->deg+1; d <= degx-shift; d++) itog(0, y->coe[d]); | |
552 | y->deg = degx - shift; | |
553 | revp(y); | |
554 | revp(x); | |
555 | } | |
556 | ||
557 | fullmod(poly x) { | |
558 | polyrem(x); | |
559 | ptop(smonic, s[0]); | |
560 | remp(x, s[0], 2); | |
561 | ptop(s[0], x); | |
562 | polyrem(x); | |
563 | } | |
564 | ||
565 | scalarmul(giant s, poly x) { | |
566 | int j; | |
567 | for(j=0; j <= x->deg; j++) { | |
568 | mulg(s, x->coe[j]); | |
569 | modg(p, x->coe[j]); | |
570 | } | |
571 | } | |
572 | ||
573 | ||
574 | schain(int el) { | |
575 | int j; | |
576 | ||
577 | s[0]->deg = 0; | |
578 | itog(0, s[0]->coe[0]); | |
579 | ||
580 | s[1]->deg = 0; | |
581 | itog(1, s[1]->coe[0]); | |
582 | s[2]->deg = 0; | |
583 | itog(2, s[2]->coe[0]); | |
584 | ||
585 | s[3]->deg = 4; | |
586 | gtog(a, aux); mulg(a, aux); negg(aux); | |
587 | gtog(aux, s[3]->coe[0]); | |
588 | gtog(b, aux); smulg(12, aux); | |
589 | gtog(aux, s[3]->coe[1]); | |
590 | gtog(a, aux); smulg(6, aux); | |
591 | gtog(aux, s[3]->coe[2]); | |
592 | itog(0, s[3]->coe[3]); | |
593 | itog(3, s[3]->coe[4]); | |
594 | ||
595 | s[4]->deg = 6; | |
596 | gtog(a, aux); mulg(a, aux); mulg(a, aux); | |
597 | gtog(b, tmp); mulg(b, tmp); smulg(8, tmp); addg(tmp, aux); | |
598 | negg(aux); | |
599 | gtog(aux, s[4]->coe[0]); | |
600 | gtog(b, aux); mulg(a, aux); smulg(4, aux); negg(aux); | |
601 | gtog(aux, s[4]->coe[1]); | |
602 | gtog(a, aux); mulg(a, aux); smulg(5, aux); negg(aux); | |
603 | gtog(aux, s[4]->coe[2]); | |
604 | gtog(b, aux); smulg(20, aux); | |
605 | gtog(aux, s[4]->coe[3]); | |
606 | gtog(a, aux); smulg(5, aux); | |
607 | gtog(aux, s[4]->coe[4]); | |
608 | itog(0, s[4]->coe[5]); | |
609 | itog(1, s[4]->coe[6]); | |
610 | itog(4, aux); | |
611 | scalarmul(aux, s[4]); | |
612 | cubic->deg = 3; | |
613 | itog(1, cubic->coe[3]); | |
614 | itog(0, cubic->coe[2]); | |
615 | gtog(a, cubic->coe[1]); | |
616 | gtog(b, cubic->coe[0]); | |
617 | for(j=5; j <= el; j++) { | |
618 | if(j % 2 == 0) { | |
619 | ptop(s[j/2 + 2], s[j]); mulp(s[j/2-1], s[j]); | |
620 | polyrem(s[j]); mulp(s[j/2-1], s[j]); polyrem(s[j]); | |
621 | ptop(s[j/2-2], s[0]); mulp(s[j/2+1], s[0]); polyrem(s[0]); | |
622 | mulp(s[j/2+1], s[0]); polyrem(s[0]); | |
623 | subp(s[0], s[j]); mulp(s[j/2], s[j]); polyrem(s[j]); | |
624 | gtog(p, aux); iaddg(1, aux); gshiftright(1, aux); | |
625 | scalarmul(aux, s[j]); | |
626 | } else { | |
627 | ptop(s[(j-1)/2+2], s[j]); | |
628 | mulp(s[(j-1)/2], s[j]); | |
629 | polyrem(s[j]); | |
630 | mulp(s[(j-1)/2], s[j]); | |
631 | polyrem(s[j]); | |
632 | mulp(s[(j-1)/2], s[j]); | |
633 | polyrem(s[j]); | |
634 | ptop(s[(j-1)/2-1], s[0]); | |
635 | mulp(s[(j-1)/2 + 1], s[0]); polyrem(s[0]); | |
636 | mulp(s[(j-1)/2 + 1], s[0]); polyrem(s[0]); | |
637 | mulp(s[(j-1)/2 + 1], s[0]); polyrem(s[0]); | |
638 | if(((j-1)/2) % 2 == 1) { | |
639 | mulp(cubic, s[0]); polyrem(s[0]); | |
640 | mulp(cubic, s[0]); polyrem(s[0]); | |
641 | } else { | |
642 | mulp(cubic, s[j]); polyrem(s[j]); | |
643 | mulp(cubic, s[j]); polyrem(s[j]); | |
644 | } | |
645 | // polyout(s[1]); polyout(s[3]); polyout(s[0]); polyout(s[j]); | |
646 | subp(s[0], s[j]); | |
647 | polyrem(s[j]); | |
648 | } | |
649 | } | |
650 | } | |
651 | ||
652 | init_recip(int el) { | |
653 | int j; | |
654 | ptop(s[el], smonic); | |
655 | if(el == 2) { | |
656 | mulp(cubic, smonic); polyrem(smonic); | |
657 | } | |
658 | gtog(smonic->coe[smonic->deg], aux); /* High coeff. */ | |
659 | binvg(p, aux); | |
660 | scalarmul(aux, smonic); /* s is now monic. */ | |
661 | s[0]->deg = smonic->deg + 1; | |
662 | for(j=0; j <= s[0]->deg; j++) itog(1, s[0]->coe[j]); | |
663 | ptop(smonic, pbuff); | |
664 | remp(s[0], pbuff, 1); /* Initialize reciprocal of s as precip. */ | |
665 | } | |
666 | ||
667 | /* void powerpoly(poly x, giant n) | |
668 | { int len, pos; | |
669 | ptop(x, pbuff); | |
670 | x->deg = 0; itog(1, x->coe[0]); | |
671 | len = bitlen(n); | |
672 | pos = 0; | |
673 | while(1) { | |
674 | if(bitval(n, pos++)) { | |
675 | mulp(pbuff, x); | |
676 | fullmod(x); | |
677 | } | |
678 | if(pos>=len) break; | |
679 | mulp(pbuff, pbuff); | |
680 | fullmod(pbuff); | |
681 | } | |
682 | } | |
683 | */ | |
684 | ||
685 | void powerpoly(poly x, giant n) | |
686 | /* Base-4 window. */ | |
687 | { int pos, code; | |
688 | ptop(x, pbuff); /* x. */ | |
689 | ptop(pbuff, pbuff2); | |
690 | mulmod(pbuff2, pbuff2); mulmod(pbuff, pbuff2); /* x^3. */ | |
691 | pos = bitlen(n)-2; | |
692 | while(pos >= 0) { | |
693 | mulmod(x, x); | |
694 | if(pos==0) { | |
695 | if(bitval(n, pos) != 0) { | |
696 | mulmod(pbuff, x); | |
697 | } | |
698 | break; | |
699 | } | |
700 | code = (bitval(n, pos) != 0) * 2 + (bitval(n, pos-1) != 0); | |
701 | switch(code) { | |
702 | case 0: mulmod(x,x); break; | |
703 | case 1: mulmod(x,x); | |
704 | mulmod(pbuff, x); | |
705 | break; | |
706 | case 2: mulmod(pbuff, x); | |
707 | mulmod(x,x); break; | |
708 | case 3: mulmod(x,x); mulmod(pbuff2, x); break; | |
709 | } | |
710 | pos -= 2; | |
711 | } | |
712 | } | |
713 | ||
714 | mulmod(poly x, poly y) { | |
715 | mulp(x, y); fullmod(y); | |
716 | } | |
717 | ||
718 | elldoublep(poly n1, poly d1, poly m1, poly c1, poly n0, poly d0, | |
719 | poly m0, poly c0) { | |
720 | ||
721 | ptop(n1, mn); mulmod(n1, mn); | |
722 | ptop(mn, pbuff); addp(mn, mn); addp(pbuff, mn); | |
723 | fullmod(mn); | |
724 | ptop(d1, pbuff); mulmod(d1, pbuff); | |
725 | scalarmul(a, pbuff); addp(pbuff, mn); | |
726 | fullmod(mn); | |
727 | mulmod(c1, mn); | |
728 | ptop(m1, md); addp(md, md); | |
729 | mulmod(d1, md); mulmod(d1, md); mulmod(cubic, md); | |
730 | ||
731 | ptop(d1, n0); mulmod(mn, n0); mulmod(mn, n0); | |
732 | mulmod(cubic, n0); | |
733 | ptop(n1, pbuff); addp(pbuff, pbuff); fullmod(pbuff); | |
734 | mulmod(md, pbuff); mulmod(md, pbuff); | |
735 | subp(pbuff, n0); fullmod(n0); | |
736 | ptop(md, d0); mulmod(md, d0); mulmod(d1, d0); | |
737 | ||
738 | ptop(mn, m0); mulmod(c1, m0); | |
739 | ptop(d0, pbuff); mulmod(n1, pbuff); | |
740 | ptop(n0, c0); mulmod(d1, c0); subp(c0, pbuff); | |
741 | fullmod(pbuff); | |
742 | mulmod(pbuff, m0); | |
743 | ptop(m1, pbuff); mulmod(md, pbuff); | |
744 | mulmod(d1, pbuff); mulmod(d0, pbuff); | |
745 | subp(pbuff, m0); fullmod(m0); | |
746 | ||
747 | ptop(c1, c0); mulmod(md, c0); mulmod(d1, c0); mulmod(d0, c0); | |
748 | } | |
749 | ||
750 | elladdp(poly n1, poly d1, poly m1, poly c1, poly n2, poly d2, poly m2, poly c2, poly n0, poly d0, poly m0, poly c0) { | |
751 | ptop(m2, mn); mulmod(c1, mn); | |
752 | ptop(m1, pbuff); mulmod(c2, pbuff); | |
753 | subp(pbuff, mn); fullmod(mn); | |
754 | mulmod(d1, mn); mulmod(d2, mn); | |
755 | ||
756 | ptop(n2, md); mulmod(d1, md); | |
757 | ptop(n1, pbuff); mulmod(d2, pbuff); | |
758 | subp(pbuff, md); fullmod(md); | |
759 | mulmod(c1, md); mulmod(c2, md); | |
760 | ||
761 | ptop(cubic, n0); mulmod(mn, n0); mulmod(mn, n0); | |
762 | mulmod(d1, n0); mulmod(d2, n0); | |
763 | ptop(n1, pbuff); mulmod(d2, pbuff); | |
764 | ptop(n2, d0); mulmod(d1, d0); | |
765 | addp(d0, pbuff); mulmod(md, pbuff); mulmod(md, pbuff); | |
766 | subp(pbuff, n0); fullmod(n0); | |
767 | ||
768 | ptop(md, d0); mulmod(md, d0); mulmod(d1, d0); mulmod(d2, d0); | |
769 | ||
770 | ptop(mn, m0); mulmod(c1, m0); | |
771 | ptop(d0, pbuff); mulmod(n1, pbuff); | |
772 | ptop(d1, c0); mulmod(n0, c0); | |
773 | subp(c0, pbuff); fullmod(pbuff); | |
774 | mulmod(pbuff, m0); | |
775 | ptop(m1, pbuff); mulmod(md, pbuff); | |
776 | mulmod(d0, pbuff); mulmod(d1, pbuff); | |
777 | subp(pbuff, m0); fullmod(m0); | |
778 | ||
779 | ptop(md, c0); mulmod(c1, c0); mulmod(d0, c0); mulmod(d1, c0); | |
780 | ||
781 | } | |
782 | ||
783 | polyout(poly x) { | |
784 | int j; | |
785 | for(j=0; j <= x->deg; j++) {printf("%d: ",j); gout(x->coe[j]);} | |
786 | } | |
787 | ||
788 | main(int argc, char **argv) { | |
789 | int j, ct = 0, el, xmatch, ymatch; | |
790 | int k, t; | |
791 | int T[L_CEIL], P[L_CEIL], LL[L_CEIL]; | |
792 | giant ss[L_CEIL]; | |
793 | unsigned int ord, ordminus; | |
794 | point_proj pt, pt2; | |
795 | ||
796 | p = newgiant(INFINITY); /* Also sets up internal giants' stacks. */ | |
797 | j = ((Q_MAX+15)/16); | |
798 | init_tools(2*j); | |
799 | a = newgiant(j); | |
800 | b = newgiant(j); | |
801 | ||
802 | entry: | |
803 | printf("Give p > 3, a, b on separate lines:\n"); fflush(stdout); | |
804 | gin(p); /* Field prime. */ | |
805 | if((Q = bitlen(p)) > Q_MAX) { | |
806 | fprintf(stderr, "p too large, larger than %d bits.\n", Q); | |
807 | goto entry; | |
808 | } | |
809 | if(!prime_probable(p)) { | |
810 | fprintf(stderr, "p is not but must be prime.\n", Q); | |
811 | goto entry; | |
812 | } | |
813 | gin(a); gin(b); /* Curve parameters. */ | |
814 | ||
815 | t1 = newgiant(2*j); | |
816 | t2 = newgiant(2*j); | |
817 | /* Next, discriminant test for legitimacy of curve. */ | |
818 | gtog(a, t1); squareg(t1); modg(p, t1); mulg(a, t1); modg(p, t1); | |
819 | gshiftleft(2, t1); /* 4 a^3 mod p. */ | |
820 | gtog(b, t2); squareg(t2); modg(p, t2); smulg(27, t2); | |
821 | addg(t2, t1); modg(p, t1); | |
822 | if(isZero(t1)) { | |
823 | fprintf(stderr, "Discriminant FAILED\n"); | |
824 | goto entry; | |
825 | } | |
826 | printf("Discriminant PASSED\n"); fflush(stdout); | |
827 | ||
828 | /* Next, find an efficient prime power array such that | |
829 | Prod[powers] >= 16 p. */ | |
830 | ||
831 | /* Create minimal prime power array such that Prod[powers]^2 > 16p. */ | |
832 | ||
833 | gtog(p, t2); gshiftleft(4, t2); /* t2 := 16p. */ | |
834 | ||
835 | L_MAX = 3; | |
836 | while(L_MAX <= L_CEIL-1) { | |
837 | for(j=0; j <= L_MAX; j++) LL[j] = 0; | |
838 | for(j=2; j <= L_MAX; j++) { | |
839 | if(primeq(j)) { | |
840 | LL[j] = 1; | |
841 | k = j; | |
842 | while(1) { | |
843 | k *= j; | |
844 | if(k <= L_MAX) { | |
845 | LL[k] = 1; | |
846 | LL[k/j] = 0; | |
847 | } | |
848 | else break; | |
849 | } | |
850 | } | |
851 | } | |
852 | itog(1, t1); | |
853 | for(j=2; j <= L_MAX; j++) { | |
854 | if(LL[j]) { smulg(j, t1); smulg(j, t1); } /* Building up t1^2. */ | |
855 | } | |
856 | if(gcompg(t1, t2) > 0) break; | |
857 | ++L_MAX; | |
858 | } | |
859 | ||
860 | printf("Initializing for prime powers:\n"); | |
861 | for(j=2; j <= L_MAX; j++) { | |
862 | if(LL[j]) printf("%d ", j); | |
863 | } | |
864 | printf("\n"); | |
865 | fflush(stdout); | |
866 | ||
867 | ||
868 | MAX_DIGS = (2 + (Q+15)/8); /* Size of (squared) coefficients. */ | |
869 | MAX_COEFFS = ((L_MAX+1)*(L_MAX+2)); | |
870 | ||
871 | init_all(); | |
872 | schain(L_MAX+1); | |
873 | ||
874 | for(L = 2; L <= L_MAX; L++) { | |
875 | if(!LL[L]) continue; | |
876 | printf("Resolving Schoof L = %d...\n", L); | |
877 | P[ct] = L; /* Stuff another prime power. */ | |
878 | init_recip(L); | |
879 | // printf("s: "); polyout(s[L]); | |
880 | gtog(p, aux2); | |
881 | k = idivg(L, aux2); /* p (mod L). */ | |
882 | ||
883 | printf("power...\n"); | |
884 | txd->deg = 0; itog(1, txd->coe[0]); | |
885 | tyd->deg = 0; itog(1, tyd->coe[0]); | |
886 | txn->deg = 1; itog(0, txn->coe[0]); itog(1, txn->coe[1]); | |
887 | ptop(txn, kxn); | |
888 | ||
889 | gtog(p, aux2); | |
890 | powerpoly(txn, aux2); /* x^p. */ | |
891 | printf("x^p done...\n"); | |
892 | ptop(txn, powx); | |
893 | powerpoly(powx, aux2); | |
894 | printf("x^p^2 done...\n"); | |
895 | ptop(cubic, tyn); | |
896 | gtog(p, aux2); itog(1, aux); subg(aux, aux2); | |
897 | gshiftright(1, aux2); /* aux2 := (p-1)/2. */ | |
898 | powerpoly(tyn, aux2); /* y^p. */ | |
899 | printf("y^p done...\n"); | |
900 | ptop(tyn, powy); mulp(tyn, powy); fullmod(powy); | |
901 | mulp(cubic, powy); fullmod(powy); | |
902 | powerpoly(powy, aux2); | |
903 | mulp(tyn, powy); fullmod(powy); | |
904 | printf("Powers done...\n"); | |
905 | ||
906 | // printf("pow" ); polyout(powx); polyout(powy); | |
907 | ptop(txn, txn1); ptop(txd, txd1); /* Save t = 1 case. */ | |
908 | ptop(tyn, tyn1); ptop(tyd, tyd1); | |
909 | /* We now shall test | |
910 | (powx, y powy) + k(kxn/kxd, y kyn/kyd) = t(txn/txd, y tyn/tyd) | |
911 | */ | |
912 | ||
913 | if(k==1) { ptop(txd, kxd); ptop(txd, kyd); | |
914 | ptop(txd, kyn); | |
915 | } else { | |
916 | ptop(s[k], kxd); mulp(s[k], kxd); fullmod(kxd); | |
917 | if(k%2==0) { mulp(cubic, kxd); fullmod(kxd); } | |
918 | mulp(kxd, kxn); fullmod(kxn); | |
919 | ptop(s[k-1], pbuff); mulp(s[k+1], pbuff); fullmod(pbuff); | |
920 | if(k%2==1) {mulp(cubic, pbuff); fullmod(pbuff); } | |
921 | subp(pbuff, kxn); fullmod(kxn); | |
922 | ||
923 | ptop(s[k+2], kyn); mulp(s[k-1], kyn); fullmod(kyn); | |
924 | mulp(s[k-1], kyn); fullmod(kyn); | |
925 | if(k > 2) { | |
926 | ptop(s[k-2], pbuff); mulp(s[k+1], pbuff); fullmod(pbuff); | |
927 | mulp(s[k+1], pbuff); fullmod(pbuff); | |
928 | subp(pbuff, kyn); fullmod(kyn); | |
929 | } | |
930 | ptop(s[k], kyd); mulp(s[k], kyd); fullmod(kyd); | |
931 | mulp(s[k], kyd); fullmod(kyd); | |
932 | if(k%2==0) { mulp(cubic, kyd); fullmod(kyd); | |
933 | mulp(cubic, kyd); fullmod(kyd);} | |
934 | itog(4, aux2); scalarmul(aux2, kyd); | |
935 | } | |
936 | //printf("kP: "); polyout(kxn); polyout(kxd); polyout(kyn); polyout(kyd); | |
937 | /* Commence t = 0 check. */ | |
938 | printf("Checking t = %d ...\n", 0); | |
939 | fflush(stdout); | |
940 | ||
941 | ptop(powx, pbuff); mulp(kxd, pbuff); | |
942 | subp(kxn, pbuff); | |
943 | fullmod(pbuff); | |
944 | ||
945 | xmatch = ymatch = 0; | |
946 | if(iszerop(pbuff)) { | |
947 | xmatch = 1; | |
948 | /* Now check y coords. */ | |
949 | if(L == 2) goto resolve; | |
950 | ptop(powy, pbuff); mulp(kyd, pbuff); | |
951 | addp(kyn, pbuff); | |
952 | fullmod(pbuff); | |
953 | if(iszerop(pbuff)) { | |
954 | resolve: | |
955 | printf("%d %d\n", L, 0); | |
956 | T[ct++] = 0; | |
957 | continue; | |
958 | } else ymatch = 1; | |
959 | } | |
960 | /* Combine pt1 and pt2. */ | |
961 | if((xmatch == 1) && (ymatch == 1)) | |
962 | elldoublep(powx, txd, powy, txd, nx, dx, ny, dy); | |
963 | else | |
964 | elladdp(powx, txd, powy, txd, kxn, kxd, kyn, kyd, nx, dx, ny, dy); | |
965 | /* Now {nx/dx, ny/dy} is (fixed) LHS. */ | |
966 | // printf("add12: "); polyout(nx); polyout(dx); polyout(ny); polyout(dy); | |
967 | /* Commence t > 0 check. */ | |
968 | for(t=1; t <= L/2; t++) { | |
969 | printf("Checking t = %d ...\n", t); | |
970 | if(t > 1) { /* Add (tx1, ty1) to (txn, tyn). */ | |
971 | ptop(txn1, pbuff); mulmod(txd, pbuff); | |
972 | ptop(txn, powx); mulmod(txd1, powx); | |
973 | subp(powx, pbuff); fullmod(pbuff); | |
974 | if(!iszerop(pbuff)) | |
975 | elladdp(txn1, txd1, tyn1, tyd1, txn, txd, tyn, tyd, | |
976 | tmp1, tmp2, tmp3, tmp4); | |
977 | else elldoublep(txn, txd, tyn, tyd, | |
978 | tmp1, tmp2, tmp3, tmp4); | |
979 | ptop(tmp1, txn); ptop(tmp2, txd); | |
980 | ptop(tmp3, tyn); ptop(tmp4, tyd); | |
981 | } | |
982 | // printf("tQ: "); polyout(txn); polyout(txd); polyout(tyn); polyout(tyd); | |
983 | /* Next, check {nx/dx, ny/dy} =? {txn/txd, tyn/tyd}. */ | |
984 | ptop(nx, pbuff); mulmod(txd, pbuff); | |
985 | ptop(dx, powx); mulmod(txn, powx); | |
986 | subp(powx, pbuff); fullmod(pbuff); | |
987 | if(!iszerop(pbuff)) continue; | |
988 | /* Next, check y. */ | |
989 | // printf("y check!\n"); | |
990 | ptop(ny, pbuff); mulmod(tyd, pbuff); | |
991 | ptop(dy, powx); mulmod(tyn, powx); | |
992 | subp(powx, pbuff); fullmod(pbuff); | |
993 | if(iszerop(pbuff)) { | |
994 | printf("%d %d\n", L, t); | |
995 | T[ct++] = t; | |
996 | } else { | |
997 | printf("%d %d\n", L, L-t); | |
998 | T[ct++] = L-t; | |
999 | } | |
1000 | fflush(stdout); | |
1001 | break; | |
1002 | } | |
1003 | } | |
1004 | ||
1005 | /* Now, prime powers P[] and CRT residues T[] are intact. */ | |
1006 | printf("Prime powers L:\n"); | |
1007 | printf("{"); | |
1008 | for(j=0; j < ct-1; j++) { | |
1009 | printf("%d, ", P[j]); | |
1010 | } | |
1011 | printf("%d }\n", P[ct-1]); | |
1012 | ||
1013 | printf("Residues t (mod L):\n"); | |
1014 | printf("{"); | |
1015 | for(j=0; j < ct-1; j++) { | |
1016 | printf("%d, ", T[j]); | |
1017 | } | |
1018 | printf("%d }\n", T[ct-1]); | |
1019 | ||
1020 | /* Mathematica algorithm for order: | |
1021 | plis = {2^5, 3^3, 5^2, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47}; | |
1022 | tlis = {1, 26, 4, 2, 4, 11, 6, 5, 19, 22, 10, 16, 7, 22, 11}; | |
1023 | prod = Apply[Times, plis]; | |
1024 | prlis = prod/plis; | |
1025 | invlis = Table[PowerMod[prlis[[q]], -1, plis[[q]]],{q,1,Length[plis]}]; | |
1026 | p = 2^127 - 1; | |
1027 | t = Mod[tlis . (prlis * invlis), prod]; | |
1028 | ord = p + 1 - If[t^2 > 4p, t - prod, t] | |
1029 | */ | |
1030 | ||
1031 | itog(1, t1); | |
1032 | for(j=0; j < ct; j++) { | |
1033 | free(s[j]); /* Just to clear memory. */ | |
1034 | smulg(P[j], t1); | |
1035 | } | |
1036 | ||
1037 | for(j=0; j < 2*ct; j++) { | |
1038 | ss[j] = newgiant(MAX_DIGS); | |
1039 | } | |
1040 | ||
1041 | for(j=0; j < ct; j++) { | |
1042 | gtog(t1, ss[j]); | |
1043 | itog(P[j], t2); | |
1044 | divg(t2, ss[j]); | |
1045 | } | |
1046 | ||
1047 | for(j=0; j < ct; j++) { | |
1048 | gtog(ss[j], ss[j+ct]); | |
1049 | itog(P[j], t2); | |
1050 | invg(t2, ss[j+ct]); | |
1051 | } | |
1052 | ||
1053 | itog(0, t4); | |
1054 | for(j=0; j < ct; j++) { | |
1055 | itog(T[j], t5); | |
1056 | mulg(ss[j], t5); | |
1057 | mulg(ss[j+ct], t5); | |
1058 | addg(t5, t4); | |
1059 | } | |
1060 | modg(t1, t4); | |
1061 | gtog(p, t5); | |
1062 | iaddg(1, t5); | |
1063 | gtog(t4, t2); | |
1064 | squareg(t4); | |
1065 | gtog(p, t3); gshiftleft(2, t3); | |
1066 | if(gcompg(t4, t3) > 0) subg(t1, t2); | |
1067 | subg(t2, t5); | |
1068 | printf("Parameters:\n"); | |
1069 | printf("p = "); gout(p); | |
1070 | printf("a = "); gout(a); | |
1071 | printf("b = "); gout(b); | |
1072 | printf("Curve order:\n"); | |
1073 | printf("o = "); gout(t5); gtog(t5, t3); /* Save order as t3. */ | |
1074 | printf("Twist order:\n"); | |
1075 | printf("o' = "); | |
1076 | addg(t2, t5); | |
1077 | addg(t2, t5); | |
1078 | gout(t5); | |
1079 | /* Next, verify the order. */ | |
1080 | printf("Verifying order o:...\n"); | |
1081 | init_ell_proj(MAX_DIGS); | |
1082 | pt = new_point_proj(MAX_DIGS); | |
1083 | pt2 = new_point_proj(MAX_DIGS); | |
1084 | itog(1,t2); | |
1085 | find_point_proj(pt, t2, a, b, p); | |
1086 | printf("A point on the curve y^2 = x^3 + a x + b (mod p) is:\n"); | |
1087 | printf("(x,y,z) = {\n"); gout(pt->x); printf(","); | |
1088 | gout(pt->y); printf(","); gout(pt->z); | |
1089 | printf("}\n"); | |
1090 | ell_mul_proj(pt, pt2, t3, a, p); | |
1091 | printf("A multiple is:\n"); | |
1092 | printf("o * (x,y,z) = {\n"); | |
1093 | gout(pt2->x); printf(",");gout(pt2->y); printf(",");gout(pt2->z); | |
1094 | printf("}\n"); | |
1095 | if(!isZero(pt2->z)) { | |
1096 | printf("TILT: multiple should be point-at-infinity.\n"); | |
1097 | exit(1); | |
1098 | } | |
1099 | printf("VERIFIED: multiple is indeed point-at-infinity.\n"); | |
1100 | } |