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