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