chiark / gitweb /
group-parse: Emit useful error messages when parsing fails.
[catacomb] / perftest.c
1 /* -*-c-*-
2  *
3  * $Id$
4  *
5  * Measure performance of various operations (Unix-specific)
6  *
7  * (c) 2004 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 /*----- Header files ------------------------------------------------------*/
31
32 #include "config.h"
33
34 #include <errno.h>
35 #include <limits.h>
36 #include <math.h>
37 #include <stdio.h>
38 #include <string.h>
39 #include <stdlib.h>
40 #include <time.h>
41
42 #include <sys/types.h>
43 #include <sys/time.h>
44 #include <unistd.h>
45
46 #include <mLib/alloc.h>
47 #include <mLib/dstr.h>
48 #include <mLib/mdwopt.h>
49 #include <mLib/quis.h>
50 #include <mLib/report.h>
51 #include <mLib/sub.h>
52 #include <mLib/tv.h>
53
54 #include "rand.h"
55 #include "mp.h"
56 #include "mprand.h"
57 #include "fibrand.h"
58 #include "rsa.h"
59 #include "mpmont.h"
60 #include "mpbarrett.h"
61 #include "dh.h"
62 #include "pgen.h"
63 #include "ec.h"
64 #include "group.h"
65
66 #include "cc.h"
67 #include "gcipher.h"
68 #include "ghash.h"
69 #include "gmac.h"
70 #include "ectab.h"
71 #include "ptab.h"
72
73 /*----- Options -----------------------------------------------------------*/
74
75 typedef struct opts {
76   const char *name;                     /* Pre-configured named thing */
77   unsigned fbits;                       /* Field size bits */
78   unsigned gbits;                       /* Group size bits */
79   unsigned n;                           /* Number of factors */
80   unsigned i;                           /* Number of intervals (or zero) */
81   double t;                             /* Time for each interval (secs) */
82 } opts;
83
84 /*----- Job switch --------------------------------------------------------*/
85
86 /* --- Barrett exponentiation --- */
87
88 typedef struct bar_ctx {
89   size_t n;
90   mpbarrett b;
91   mp_expfactor *e;
92 } bar_ctx;
93
94 static void *bar_init(opts *o)
95 {
96   bar_ctx *c = CREATE(bar_ctx);
97   gprime_param gp;
98   qd_parse qd;
99   size_t i;
100
101   if (o->name) {
102     qd.p = o->name;
103     if (dh_parse(&qd, &gp))
104       die(1, "bad prime group: %s", qd.e);
105   } else {
106     if (!o->fbits) o->fbits = 1024;
107     dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
108   }
109   mpbarrett_create(&c->b, gp.p);
110   if (!o->n) o->n = 1;
111   c->n = o->n;
112   c->e = xmalloc(c->n * sizeof(group_expfactor));
113   for (i = 0; i < c->n; i++) {
114     c->e[i].base = mprand_range(MP_NEW, gp.p, &rand_global, 0);
115     c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
116   }
117   dh_paramfree(&gp);
118   return (c);
119 }
120
121 static void bar_run(void *cc)
122 {
123   bar_ctx *c = cc;
124   mp *d = mpbarrett_exp(&c->b, MP_NEW, c->e[0].base, c->e[0].exp);
125   MP_DROP(d);
126 }
127
128 static void barsim_run(void *cc)
129 {
130   bar_ctx *c = cc;
131   mp *d = mpbarrett_mexp(&c->b, MP_NEW, c->e, c->n);
132   MP_DROP(d);
133 }
134
135 /* --- Montgomery exponentiation --- */
136
137 typedef struct mont_ctx {
138   size_t n;
139   mpmont m;
140   mp_expfactor *e;
141 } mont_ctx;
142
143 static void *mont_init(opts *o)
144 {
145   mont_ctx *c = CREATE(mont_ctx);
146   gprime_param gp;
147   qd_parse qd;
148   size_t i;
149
150   if (o->name) {
151     qd.p = o->name;
152     if (dh_parse(&qd, &gp))
153       die(1, "bad prime group: %s", qd.e);
154   } else {
155     if (!o->fbits) o->fbits = 1024;
156     dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
157   }
158   mpmont_create(&c->m, gp.p);
159   if (!o->n) o->n = 1;
160   c->n = o->n;
161   c->e = xmalloc(c->n * sizeof(mp_expfactor));
162   for (i = 0; i < c->n; i++) {
163     c->e[i].base = mprand_range(MP_NEW, gp.p, &rand_global, 0);
164     c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
165   }
166   dh_paramfree(&gp);
167   return (c);
168 }
169
170 static void mont_run(void *cc)
171 {
172   mont_ctx *c = cc;
173   mp *d = mpmont_expr(&c->m, MP_NEW, c->e[0].base, c->e[0].exp);
174   MP_DROP(d);
175 }
176
177 static void montsim_run(void *cc)
178 {
179   mont_ctx *c = cc;
180   mp *d = mpmont_mexpr(&c->m, MP_NEW, c->e, c->n);
181   MP_DROP(d);
182 }
183
184 /* --- Group exponentiation --- */
185
186 typedef struct gr_ctx {
187   size_t n;
188   group *g;
189   group_expfactor *e;
190 } gr_ctx;
191
192 static void *grp_init(opts *o)
193 {
194   gr_ctx *c = CREATE(gr_ctx);
195   const char *e;
196   gprime_param gp;
197   qd_parse qd;
198   size_t i;
199
200   if (o->name) {
201     qd.p = o->name;
202     if (dh_parse(&qd, &gp))
203       die(1, "bad prime group: %s", qd.e);
204   } else {
205     if (!o->fbits) o->fbits = 1024;
206     dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
207   }
208   c->g = group_prime(&gp);
209   if ((e = G_CHECK(c->g, &rand_global)) != 0)
210     die(1, "bad group: %s", e);
211   if (!o->n) o->n = 1;
212   c->n = o->n;
213   c->e = xmalloc(c->n * sizeof(group_expfactor));
214   for (i = 0; i < c->n; i++) {
215     c->e[i].base = G_CREATE(c->g);
216     G_FROMINT(c->g, c->e[i].base,
217               mprand_range(MP_NEW, gp.p, &rand_global, 0));
218     c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
219   }
220   dh_paramfree(&gp);
221   return (c);
222 }
223
224 static void *grec_init(opts *o)
225 {
226   gr_ctx *c = CREATE(gr_ctx);
227   const char *e;
228   ec_info ei;
229   ec p = EC_INIT;
230   size_t i;
231
232   if (!o->name)
233     die(1, "can't generate elliptic curves");
234   if ((e = ec_getinfo(&ei, o->name)) != 0)
235     die(1, "bad curve: %s", e);
236   c->g = group_ec(&ei);
237   if ((e = G_CHECK(c->g, &rand_global)) != 0)
238     die(1, "bad group: %s", e);
239   if (!o->n) o->n = 1;
240   c->n = o->n;
241   c->e = xmalloc(c->n * sizeof(group_expfactor));
242   for (i = 0; i < c->n; i++) {
243     c->e[i].base = G_CREATE(c->g);
244     ec_rand(ei.c, &p, &rand_global);
245     G_FROMEC(c->g, c->e[i].base, &p);
246     c->e[i].exp = mprand_range(MP_NEW, ei.r, &rand_global, 0);
247   }
248   EC_DESTROY(&p);
249   return (c);
250 }
251
252 static void gr_run(void *cc)
253 {
254   gr_ctx *c = cc;
255   ge *x = G_CREATE(c->g);
256   G_EXP(c->g, x, c->e[0].base, c->e[0].exp);
257   G_DESTROY(c->g, x);
258 }
259
260 static void grsim_run(void *cc)
261 {
262   gr_ctx *c = cc;
263   ge *x = G_CREATE(c->g);
264   G_MEXP(c->g, x, c->e, c->n);
265   G_DESTROY(c->g, x);
266 }
267
268 /* --- RSA --- */
269
270 typedef struct rsapriv_ctx {
271   rsa_priv rp;
272   rsa_privctx rpc;
273   mp *m;
274 } rsapriv_ctx;
275
276 static void *rsapriv_init(opts *o)
277 {
278   rsapriv_ctx *c = CREATE(rsapriv_ctx);
279
280   if (!o->fbits) o->fbits = 1024;
281   rsa_gen(&c->rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
282   rsa_privcreate(&c->rpc, &c->rp, 0);
283   c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
284   return (c);
285 }
286
287 static void *rsaprivblind_init(opts *o)
288 {
289   rsapriv_ctx *c = CREATE(rsapriv_ctx);
290
291   if (!o->fbits) o->fbits = 1024;
292   rsa_gen(&c->rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
293   rsa_privcreate(&c->rpc, &c->rp, fibrand_create(0));
294   c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
295   return (c);
296 }
297
298 static void rsapriv_run(void *cc)
299 {
300   rsapriv_ctx *c = cc;
301   mp *d = rsa_privop(&c->rpc, MP_NEW, c->m);
302   MP_DROP(d);
303 }
304
305 typedef struct rsapub_ctx {
306   rsa_pub rp;
307   rsa_pubctx rpc;
308   mp *m;
309 } rsapub_ctx;
310
311 static void *rsapub_init(opts *o)
312 {
313   rsapub_ctx *c = CREATE(rsapub_ctx);
314   rsa_priv rp;
315
316   if (!o->fbits) o->fbits = 1024;
317   rsa_gen(&rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
318   c->rp.n = MP_COPY(rp.n);
319   c->rp.e = MP_COPY(rp.e);
320   rsa_privfree(&rp);
321   rsa_pubcreate(&c->rpc, &c->rp);
322   c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
323   return (c);
324 }
325
326 static void rsapub_run(void *cc)
327 {
328   rsapub_ctx *c = cc;
329   mp *d = rsa_pubop(&c->rpc, MP_NEW, c->m);
330   MP_DROP(d);
331 }
332
333 /* --- Symmetric encryption --- */
334
335 typedef struct ksched_ctx {
336   const gccipher *c;
337   octet *k;
338   size_t ksz;
339 } ksched_ctx;
340
341 static void *ksched_init(opts *o)
342 {
343   ksched_ctx *c = CREATE(ksched_ctx);
344   if (!o->name)
345     die(1, "must specify encryption scheme name");
346   if ((c->c = gcipher_byname(o->name)) == 0)
347     die(1, "encryption scheme `%s' not known", o->name);
348   c->ksz = keysz(o->gbits/8, c->c->keysz);
349   c->k = xmalloc(c->ksz);
350   rand_get(RAND_GLOBAL, c->k, c->ksz);
351   return (c);
352 }
353
354 static void ksched_run(void *cc)
355 {
356   ksched_ctx *c = cc;
357   gcipher *gc = GC_INIT(c->c, c->k, c->ksz);
358   GC_DESTROY(gc);
359 }
360
361 typedef struct enc_ctx {
362   gcipher *c;
363   octet *m;
364   size_t sz;
365   size_t n;
366 } enc_ctx;
367
368 static void *enc_init(opts *o)
369 {
370   enc_ctx *c = CREATE(enc_ctx);
371   const gccipher *cc;
372   size_t ksz;
373   octet *k;
374   if (!o->name)
375     die(1, "must specify encryption scheme name");
376   if ((cc = gcipher_byname(o->name)) == 0)
377     die(1, "encryption scheme `%s' not known", o->name);
378   ksz = keysz(0, cc->keysz);
379   k = xmalloc(ksz);
380   rand_get(RAND_GLOBAL, k, ksz);
381   c->c = GC_INIT(cc, k, ksz);
382   xfree(k);
383   c->sz = o->gbits ? o->gbits : 65536;
384   c->n = o->n ? o->n : 16;
385   c->m = xmalloc(c->sz);
386   return (c);
387 }
388
389 static void enc_run(void *cc)
390 {
391   enc_ctx *c = cc;
392   size_t i;
393   for (i = 0; i < c->n; i++)
394     GC_ENCRYPT(c->c, c->m, c->m, c->sz);
395 }
396
397 /* --- Hashing --- */
398
399 typedef struct hash_ctx {
400   const gchash *h;
401   octet *m;
402   size_t sz;
403   size_t n;
404 } hash_ctx;
405
406 static void *hash_init(opts *o)
407 {
408   hash_ctx *c = CREATE(hash_ctx);
409   if (!o->name)
410     die(1, "must specify hash function name");
411   if ((c->h = ghash_byname(o->name)) == 0)
412     die(1, "hash function `%s' not known", o->name);
413   c->sz = o->gbits ? o->gbits : 65536;
414   c->n = o->n ? o->n : 16;
415   c->m = xmalloc(c->sz);
416   return (c);
417 }
418
419 static void hash_run(void *cc)
420 {
421   hash_ctx *c = cc;
422   size_t i;
423   ghash *h = GH_INIT(c->h);
424   for (i = 0; i < c->n; i++)
425     GH_HASH(h, c->m, c->sz);
426   GH_DONE(h, 0);
427   GH_DESTROY(h);
428 }
429
430 /* --- Job table --- */
431
432 typedef struct jobops {
433   const char *name;
434   void *(*init)(opts *);
435   void (*run)(void *);
436 } jobops;
437
438 static const jobops jobtab[] = {
439   { "g-prime-exp",              grp_init,               gr_run },
440   { "g-ec-mul",                 grec_init,              gr_run },
441   { "g-prime-exp-sim",          grp_init,               grsim_run },
442   { "g-ec-mul-sim",             grec_init,              grsim_run },
443   { "barrett-exp",              bar_init,               bar_run },
444   { "barrett-exp-sim",          bar_init,               barsim_run },
445   { "mont-exp",                 mont_init,              mont_run },
446   { "mont-exp-sim",             mont_init,              montsim_run },
447   { "rsa-priv",                 rsapriv_init,           rsapriv_run },
448   { "rsa-priv-blind",           rsaprivblind_init,      rsapriv_run },
449   { "rsa-pub",                  rsapub_init,            rsapub_run },
450   { "ksched",                   ksched_init,            ksched_run },
451   { "enc",                      enc_init,               enc_run },
452   { "hash",                     hash_init,              hash_run },
453   { 0,                          0,                      0 }
454 };
455
456 /*----- Main code ---------------------------------------------------------*/
457
458 void version(FILE *fp)
459 {
460   pquis(fp, "$, Catacomb " VERSION "\n");
461 }
462
463 static void usage(FILE *fp)
464 {
465   pquis(fp, "Usage: $ [-options] job\n");
466 }
467
468 static void help(FILE *fp)
469 {
470   version(fp);
471   putc('\n', fp);
472   usage(fp);
473   pquis(fp, "\n\
474 Various performance tests.\n\
475 \n\
476 Options:\n\
477 \n\
478 -h, --help              Show this help text.\n\
479 -v, --version           Show program version number.\n\
480 -u, --usage             Show terse usage message.\n\
481 -l, --list [ITEM...]    List all the various names of things.\n\
482 \n\
483 -C, --name=NAME         Select curve/DH-group/enc/hash name.\n\
484 -b, --field-bits        Field size for g-prime and rsa.\n\
485 -B, --group-bits        Group size for g-prime; key size for ksched;\n\
486                           data size for enc and hash.\n\
487 -n, --factors=COUNT     Number of factors for {exp,mul}-sim.\n\
488 -i, --intervals=COUNT   Number of intervals to run for.  [0; forever]\n\
489 -t, --time=TIME         Length of an interval in seconds.  [1]\n\
490 ");
491 }
492
493 #define LISTS(LI)                                                       \
494   LI("Lists", list,                                                     \
495      listtab[i].name, listtab[i].name)                                  \
496   LI("Jobs", job,                                                       \
497      jobtab[i].name, jobtab[i].name)                                    \
498   LI("Elliptic curves", ec,                                             \
499      ectab[i].name, ectab[i].name)                                      \
500   LI("Diffie-Hellman groups", dh,                                       \
501      ptab[i].name, ptab[i].name)                                        \
502   LI("Encryption algorithms", cipher,                                   \
503      gciphertab[i], gciphertab[i]->name)                                \
504   LI("Hash functions", hash,                                            \
505      ghashtab[i], ghashtab[i]->name)
506
507 MAKELISTTAB(listtab, LISTS)
508
509 static unsigned uarg(const char *what, const char *p)
510 {
511   char *q;
512   unsigned long u;
513   errno = 0;
514   u = strtoul(p, &q, 0);
515   if (*q || u > UINT_MAX || q == p || errno)
516     die(1, "bad %s `%s'", what, p);
517   return (u);
518 }
519
520 static double farg(const char *what, const char *p)
521 {
522   char *q;
523   double f;
524   errno = 0;
525   f = strtod(p, &q);
526   if (*q || q == p || errno)
527     die(1, "bad %s `%s'", what, p);
528   return (f);
529 }
530
531 int main(int argc, char *argv[])
532 {
533   int i;
534   opts o = { 0 };
535   const jobops *j;
536   struct timeval tv_next, tv_now;
537   double t, ttot;
538   unsigned n;
539   unsigned long ii;
540   clock_t c_start, c_stop;
541   double itot;
542   void *p;
543
544   ego(argv[0]);
545   o.t = 1;
546   for (;;) {
547     static const struct option opts[] = {
548       { "help",         0,              0,      'h' },
549       { "version",      0,              0,      'v' },
550       { "usage",        0,              0,      'u' },
551       { "list",         0,              0,      'l' },
552       { "name",         OPTF_ARGREQ,    0,      'C' },
553       { "field-bits",   OPTF_ARGREQ,    0,      'b' },
554       { "group-bits",   OPTF_ARGREQ,    0,      'B' },
555       { "factors",      OPTF_ARGREQ,    0,      'n' },
556       { "intervals",    OPTF_ARGREQ,    0,      'i' },
557       { "time",         OPTF_ARGREQ,    0,      't' },
558       { 0,              0,              0,      0 }
559     };
560
561     i = mdwopt(argc, argv, "hvulC:b:B:n:i:t:", opts, 0, 0, 0);
562     if (i < 0) break;
563     switch (i) {
564       case 'h': help(stdout); exit(0);
565       case 'v': version(stdout); exit(0);
566       case 'u': usage(stdout); exit(0);
567       case 'l': exit(displaylists(listtab, argv + optind));
568       case 'C': o.name = optarg; break;
569       case 'b': o.fbits = uarg("field bits", optarg); break;
570       case 'B': o.gbits = uarg("subgroup bits", optarg); break;
571       case 'n': o.n = uarg("factor count", optarg); break;
572       case 'i': o.i = uarg("interval count", optarg); break;
573       case 't': o.t = farg("interval length", optarg); break;
574       default: usage(stderr); exit(1);
575     }
576   }
577   if (optind + 1 != argc) { usage(stderr); exit(1); }
578
579   for (j = jobtab; j->name; j++)
580     if (strcmp(j->name, argv[optind]) == 0) break;
581   if (!j->name) die(1, "unknown job type `%s'", argv[optind]);
582   p = j->init(&o);
583
584   n = 0;
585   ttot = itot =  0;
586   gettimeofday(&tv_now, 0);
587   do {
588     tv_addl(&tv_next, &tv_now, o.t, fmod(o.t * MILLION, MILLION));
589     ii = 0;
590     c_start = clock();
591     do {
592       j->run(p);
593       ii++;
594       gettimeofday(&tv_now, 0);
595     } while (TV_CMP(&tv_now, <, &tv_next));
596     c_stop = clock();
597     t = (double)(c_stop - c_start)/CLOCKS_PER_SEC;
598     itot += ii;
599     ttot += t;
600     printf("%5u: did = %5lu; /sec = %5f; avg /sec = %5f\n",
601            n, ii, ii/t, itot/ttot);
602     fflush(stdout);
603     n++;
604   } while (!o.i || n < o.i);
605
606   return (0);
607 }
608
609 /*----- That's all, folks -------------------------------------------------*/