pub/, progs/: Implement Bernstein's X25519 key-exchange algorithm.
[catacomb] / progs / perftest.c
1 /* -*-c-*-
2 *
3 * Measure performance of various operations (Unix-specific)
4 *
5 * (c) 2004 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of Catacomb.
11 *
12 * Catacomb is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU Library General Public License as
14 * published by the Free Software Foundation; either version 2 of the
15 * License, or (at your option) any later version.
16 *
17 * Catacomb is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU Library General Public License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Catacomb; if not, write to the Free
24 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
25 * MA 02111-1307, USA.
26 */
27
28 /*----- Header files ------------------------------------------------------*/
29
30 #define _FILE_OFFSET_BITS 64
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 #include "x25519.h"
66
67 #include "cc.h"
68 #include "gcipher.h"
69 #include "ghash.h"
70 #include "gmac.h"
71 #include "poly1305.h"
72
73 #include "ectab.h"
74 #include "ptab.h"
75
76 /*----- Options -----------------------------------------------------------*/
77
78 typedef struct opts {
79 const char *name; /* Pre-configured named thing */
80 unsigned fbits; /* Field size bits */
81 unsigned gbits; /* Group size bits */
82 unsigned n; /* Number of factors */
83 unsigned i; /* Number of intervals (or zero) */
84 double t; /* Time for each interval (secs) */
85 unsigned f; /* Flags */
86 #define OF_NOCHECK 1u /* Don't do group checking */
87 } opts;
88
89 /*----- Job switch --------------------------------------------------------*/
90
91 /* --- Barrett exponentiation --- */
92
93 typedef struct bar_ctx {
94 size_t n;
95 mpbarrett b;
96 mp_expfactor *e;
97 } bar_ctx;
98
99 static void *bar_init(opts *o)
100 {
101 bar_ctx *c = CREATE(bar_ctx);
102 gprime_param gp;
103 qd_parse qd;
104 size_t i;
105
106 if (o->name) {
107 qd.p = o->name;
108 if (dh_parse(&qd, &gp))
109 die(1, "bad prime group: %s", qd.e);
110 } else {
111 if (!o->fbits) o->fbits = 1024;
112 dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
113 }
114 mpbarrett_create(&c->b, gp.p);
115 if (!o->n) o->n = 1;
116 c->n = o->n;
117 c->e = xmalloc(c->n * sizeof(group_expfactor));
118 for (i = 0; i < c->n; i++) {
119 c->e[i].base = mprand_range(MP_NEW, gp.p, &rand_global, 0);
120 c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
121 }
122 dh_paramfree(&gp);
123 return (c);
124 }
125
126 static void bar_run(void *cc)
127 {
128 bar_ctx *c = cc;
129 mp *d = mpbarrett_exp(&c->b, MP_NEW, c->e[0].base, c->e[0].exp);
130 MP_DROP(d);
131 }
132
133 static void barsim_run(void *cc)
134 {
135 bar_ctx *c = cc;
136 mp *d = mpbarrett_mexp(&c->b, MP_NEW, c->e, c->n);
137 MP_DROP(d);
138 }
139
140 /* --- Montgomery exponentiation --- */
141
142 typedef struct mont_ctx {
143 size_t n;
144 mpmont m;
145 mp_expfactor *e;
146 } mont_ctx;
147
148 static void *mont_init(opts *o)
149 {
150 mont_ctx *c = CREATE(mont_ctx);
151 gprime_param gp;
152 qd_parse qd;
153 size_t i;
154
155 if (o->name) {
156 qd.p = o->name;
157 if (dh_parse(&qd, &gp))
158 die(1, "bad prime group: %s", qd.e);
159 } else {
160 if (!o->fbits) o->fbits = 1024;
161 dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
162 }
163 mpmont_create(&c->m, gp.p);
164 if (!o->n) o->n = 1;
165 c->n = o->n;
166 c->e = xmalloc(c->n * sizeof(mp_expfactor));
167 for (i = 0; i < c->n; i++) {
168 c->e[i].base = mprand_range(MP_NEW, gp.p, &rand_global, 0);
169 c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
170 }
171 dh_paramfree(&gp);
172 return (c);
173 }
174
175 static void mont_run(void *cc)
176 {
177 mont_ctx *c = cc;
178 mp *d = mpmont_expr(&c->m, MP_NEW, c->e[0].base, c->e[0].exp);
179 MP_DROP(d);
180 }
181
182 static void montsim_run(void *cc)
183 {
184 mont_ctx *c = cc;
185 mp *d = mpmont_mexpr(&c->m, MP_NEW, c->e, c->n);
186 MP_DROP(d);
187 }
188
189 /* --- Group exponentiation --- */
190
191 typedef struct gr_ctx {
192 size_t n;
193 group *g;
194 group_expfactor *e;
195 } gr_ctx;
196
197 static void *grp_init(opts *o)
198 {
199 gr_ctx *c = CREATE(gr_ctx);
200 const char *e;
201 gprime_param gp;
202 qd_parse qd;
203 size_t i;
204
205 if (o->name) {
206 qd.p = o->name;
207 if (dh_parse(&qd, &gp))
208 die(1, "bad prime group: %s", qd.e);
209 } else {
210 if (!o->fbits) o->fbits = 1024;
211 dh_gen(&gp, o->gbits, o->fbits, 0, &rand_global, pgen_evspin, 0);
212 }
213 c->g = group_prime(&gp);
214 if (!(o->f & OF_NOCHECK) && (e = G_CHECK(c->g, &rand_global)) != 0)
215 die(1, "bad group: %s", e);
216 if (!o->n) o->n = 1;
217 c->n = o->n;
218 c->e = xmalloc(c->n * sizeof(group_expfactor));
219 for (i = 0; i < c->n; i++) {
220 c->e[i].base = G_CREATE(c->g);
221 G_FROMINT(c->g, c->e[i].base,
222 mprand_range(MP_NEW, gp.p, &rand_global, 0));
223 c->e[i].exp = mprand_range(MP_NEW, gp.q, &rand_global, 0);
224 }
225 dh_paramfree(&gp);
226 return (c);
227 }
228
229 static void *grec_init(opts *o)
230 {
231 gr_ctx *c = CREATE(gr_ctx);
232 const char *e;
233 ec_info ei;
234 ec p = EC_INIT;
235 size_t i;
236
237 if (!o->name)
238 die(1, "can't generate elliptic curves");
239 if ((e = ec_getinfo(&ei, o->name)) != 0)
240 die(1, "bad curve: %s", e);
241 c->g = group_ec(&ei);
242 if (!(o->f & OF_NOCHECK) && (e = G_CHECK(c->g, &rand_global)) != 0)
243 die(1, "bad group: %s", e);
244 if (!o->n) o->n = 1;
245 c->n = o->n;
246 c->e = xmalloc(c->n * sizeof(group_expfactor));
247 for (i = 0; i < c->n; i++) {
248 c->e[i].base = G_CREATE(c->g);
249 ec_rand(ei.c, &p, &rand_global);
250 G_FROMEC(c->g, c->e[i].base, &p);
251 c->e[i].exp = mprand_range(MP_NEW, ei.r, &rand_global, 0);
252 }
253 EC_DESTROY(&p);
254 return (c);
255 }
256
257 static void gr_run(void *cc)
258 {
259 gr_ctx *c = cc;
260 ge *x = G_CREATE(c->g);
261 G_EXP(c->g, x, c->e[0].base, c->e[0].exp);
262 G_DESTROY(c->g, x);
263 }
264
265 static void grsim_run(void *cc)
266 {
267 gr_ctx *c = cc;
268 ge *x = G_CREATE(c->g);
269 G_MEXP(c->g, x, c->e, c->n);
270 G_DESTROY(c->g, x);
271 }
272
273 /* --- x25519 --- */
274
275 typedef struct x25519_jobctx {
276 octet k[X25519_KEYSZ];
277 octet p[X25519_PUBSZ];
278 } x25519_jobctx;
279
280 static void *x25519_jobinit(opts *o)
281 {
282 x25519_jobctx *c = CREATE(x25519_jobctx);
283 rand_get(RAND_GLOBAL, c->k, sizeof(c->k));
284 rand_get(RAND_GLOBAL, c->p, sizeof(c->p));
285 return (c);
286 }
287
288 static void x25519_jobrun(void *cc)
289 { x25519_jobctx *c = cc; octet z[X25519_OUTSZ]; x25519(z, c->k, c->p); }
290
291 /* --- RSA --- */
292
293 typedef struct rsapriv_ctx {
294 rsa_priv rp;
295 rsa_privctx rpc;
296 mp *m;
297 } rsapriv_ctx;
298
299 static void *rsapriv_init(opts *o)
300 {
301 rsapriv_ctx *c = CREATE(rsapriv_ctx);
302
303 if (!o->fbits) o->fbits = 1024;
304 rsa_gen(&c->rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
305 rsa_privcreate(&c->rpc, &c->rp, 0);
306 c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
307 return (c);
308 }
309
310 static void *rsaprivblind_init(opts *o)
311 {
312 rsapriv_ctx *c = CREATE(rsapriv_ctx);
313
314 if (!o->fbits) o->fbits = 1024;
315 rsa_gen(&c->rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
316 rsa_privcreate(&c->rpc, &c->rp, fibrand_create(0));
317 c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
318 return (c);
319 }
320
321 static void rsapriv_run(void *cc)
322 {
323 rsapriv_ctx *c = cc;
324 mp *d = rsa_privop(&c->rpc, MP_NEW, c->m);
325 MP_DROP(d);
326 }
327
328 typedef struct rsapub_ctx {
329 rsa_pub rp;
330 rsa_pubctx rpc;
331 mp *m;
332 } rsapub_ctx;
333
334 static void *rsapub_init(opts *o)
335 {
336 rsapub_ctx *c = CREATE(rsapub_ctx);
337 rsa_priv rp;
338
339 if (!o->fbits) o->fbits = 1024;
340 rsa_gen(&rp, o->fbits, &rand_global, 0, pgen_evspin, 0);
341 c->rp.n = MP_COPY(rp.n);
342 c->rp.e = MP_COPY(rp.e);
343 rsa_privfree(&rp);
344 rsa_pubcreate(&c->rpc, &c->rp);
345 c->m = mprand_range(MP_NEW, c->rp.n, &rand_global, 0);
346 return (c);
347 }
348
349 static void rsapub_run(void *cc)
350 {
351 rsapub_ctx *c = cc;
352 mp *d = rsa_pubop(&c->rpc, MP_NEW, c->m);
353 MP_DROP(d);
354 }
355
356 /* --- Symmetric encryption --- */
357
358 typedef struct ksched_ctx {
359 const gccipher *c;
360 octet *k;
361 size_t ksz;
362 } ksched_ctx;
363
364 static void *ksched_init(opts *o)
365 {
366 ksched_ctx *c = CREATE(ksched_ctx);
367 if (!o->name)
368 die(1, "must specify encryption scheme name");
369 if ((c->c = gcipher_byname(o->name)) == 0)
370 die(1, "encryption scheme `%s' not known", o->name);
371 c->ksz = keysz(o->gbits/8, c->c->keysz);
372 c->k = xmalloc(c->ksz);
373 rand_get(RAND_GLOBAL, c->k, c->ksz);
374 return (c);
375 }
376
377 static void ksched_run(void *cc)
378 {
379 ksched_ctx *c = cc;
380 gcipher *gc = GC_INIT(c->c, c->k, c->ksz);
381 GC_DESTROY(gc);
382 }
383
384 typedef struct enc_ctx {
385 gcipher *c;
386 octet *m;
387 size_t sz;
388 size_t n;
389 } enc_ctx;
390
391 static void *enc_init(opts *o)
392 {
393 enc_ctx *c = CREATE(enc_ctx);
394 const gccipher *cc;
395 size_t ksz;
396 octet *k;
397 if (!o->name)
398 die(1, "must specify encryption scheme name");
399 if ((cc = gcipher_byname(o->name)) == 0)
400 die(1, "encryption scheme `%s' not known", o->name);
401 ksz = keysz(0, cc->keysz);
402 k = xmalloc(ksz);
403 rand_get(RAND_GLOBAL, k, ksz);
404 c->c = GC_INIT(cc, k, ksz);
405 xfree(k);
406 c->sz = o->gbits ? o->gbits : 65536;
407 c->n = o->n ? o->n : 16;
408 c->m = xmalloc(c->sz);
409 return (c);
410 }
411
412 static void enc_run(void *cc)
413 {
414 enc_ctx *c = cc;
415 size_t i;
416 for (i = 0; i < c->n; i++)
417 GC_ENCRYPT(c->c, c->m, c->m, c->sz);
418 }
419
420 /* --- Hashing --- */
421
422 typedef struct hash_ctx {
423 const gchash *h;
424 octet *m;
425 size_t sz;
426 size_t n;
427 } hash_ctx;
428
429 static void *hash_init(opts *o)
430 {
431 hash_ctx *c = CREATE(hash_ctx);
432 if (!o->name)
433 die(1, "must specify hash function name");
434 if ((c->h = ghash_byname(o->name)) == 0)
435 die(1, "hash function `%s' not known", o->name);
436 c->sz = o->gbits ? o->gbits : 65536;
437 c->n = o->n ? o->n : 16;
438 c->m = xmalloc(c->sz);
439 return (c);
440 }
441
442 static void hash_run(void *cc)
443 {
444 hash_ctx *c = cc;
445 size_t i;
446 ghash *h = GH_INIT(c->h);
447 for (i = 0; i < c->n; i++)
448 GH_HASH(h, c->m, c->sz);
449 GH_DONE(h, 0);
450 GH_DESTROY(h);
451 }
452
453 /* --- Poly1305 --- */
454
455 typedef struct poly1305_jobctx {
456 poly1305_key k;
457 octet s[POLY1305_MASKSZ];
458 octet *m;
459 size_t sz;
460 size_t n;
461 } poly1305_jobctx;
462
463 static void *poly1305_jobinit(opts *o)
464 {
465 octet k[POLY1305_KEYSZ];
466 poly1305_jobctx *c = CREATE(poly1305_jobctx);
467 rand_get(RAND_GLOBAL, k, sizeof(k));
468 poly1305_keyinit(&c->k, k, sizeof(k));
469 rand_get(RAND_GLOBAL, c->s, sizeof(c->s));
470 c->sz = o->gbits ? o->gbits : 65536;
471 c->n = o->n ? o->n : 16;
472 c->m = xmalloc(c->sz);
473 return (c);
474 }
475
476 static void poly1305_jobrun(void *cc)
477 {
478 poly1305_jobctx *c = cc;
479 poly1305_ctx ctx;
480 octet t[POLY1305_TAGSZ];
481 size_t i;
482 poly1305_macinit(&ctx, &c->k, c->s);
483 for (i = 0; i < c->n; i++) poly1305_hash(&ctx, c->m, c->sz);
484 poly1305_done(&ctx, t);
485 }
486
487 /* --- Job table --- */
488
489 typedef struct jobops {
490 const char *name;
491 void *(*init)(opts *);
492 void (*run)(void *);
493 } jobops;
494
495 static const jobops jobtab[] = {
496 { "g-prime-exp", grp_init, gr_run },
497 { "g-ec-mul", grec_init, gr_run },
498 { "g-prime-exp-sim", grp_init, grsim_run },
499 { "g-ec-mul-sim", grec_init, grsim_run },
500 { "barrett-exp", bar_init, bar_run },
501 { "barrett-exp-sim", bar_init, barsim_run },
502 { "mont-exp", mont_init, mont_run },
503 { "mont-exp-sim", mont_init, montsim_run },
504 { "rsa-priv", rsapriv_init, rsapriv_run },
505 { "rsa-priv-blind", rsaprivblind_init, rsapriv_run },
506 { "rsa-pub", rsapub_init, rsapub_run },
507 { "x25519", x25519_jobinit, x25519_jobrun },
508 { "ksched", ksched_init, ksched_run },
509 { "enc", enc_init, enc_run },
510 { "hash", hash_init, hash_run },
511 { "poly1305", poly1305_jobinit, poly1305_jobrun },
512 { 0, 0, 0 }
513 };
514
515 /*----- Main code ---------------------------------------------------------*/
516
517 void version(FILE *fp)
518 {
519 pquis(fp, "$, Catacomb " VERSION "\n");
520 }
521
522 static void usage(FILE *fp)
523 {
524 pquis(fp, "Usage: $ [-options] job\n");
525 }
526
527 static void help(FILE *fp)
528 {
529 version(fp);
530 putc('\n', fp);
531 usage(fp);
532 pquis(fp, "\n\
533 Various performance tests.\n\
534 \n\
535 Options:\n\
536 \n\
537 -h, --help Show this help text.\n\
538 -v, --version Show program version number.\n\
539 -u, --usage Show terse usage message.\n\
540 -l, --list [ITEM...] List all the various names of things.\n\
541 \n\
542 -C, --name=NAME Select curve/DH-group/enc/hash name.\n\
543 -b, --field-bits Field size for g-prime and rsa.\n\
544 -q, --no-check Don't check field/group for validity.\n\
545 -B, --group-bits Group size for g-prime; key size for ksched;\n\
546 data size for enc and hash.\n\
547 -n, --factors=COUNT Number of factors for {exp,mul}-sim.\n\
548 -i, --intervals=COUNT Number of intervals to run for. [0; forever]\n\
549 -t, --time=TIME Length of an interval in seconds. [1]\n\
550 ");
551 }
552
553 #define LISTS(LI) \
554 LI("Lists", list, \
555 listtab[i].name, listtab[i].name) \
556 LI("Jobs", job, \
557 jobtab[i].name, jobtab[i].name) \
558 LI("Elliptic curves", ec, \
559 ectab[i].name, ectab[i].name) \
560 LI("Diffie-Hellman groups", dh, \
561 ptab[i].name, ptab[i].name) \
562 LI("Encryption algorithms", cipher, \
563 gciphertab[i], gciphertab[i]->name) \
564 LI("Hash functions", hash, \
565 ghashtab[i], ghashtab[i]->name)
566
567 MAKELISTTAB(listtab, LISTS)
568
569 static unsigned uarg(const char *what, const char *p)
570 {
571 char *q;
572 unsigned long u;
573 errno = 0;
574 u = strtoul(p, &q, 0);
575 if (*q || u > UINT_MAX || q == p || errno)
576 die(1, "bad %s `%s'", what, p);
577 return (u);
578 }
579
580 static double farg(const char *what, const char *p)
581 {
582 char *q;
583 double f;
584 errno = 0;
585 f = strtod(p, &q);
586 if (*q || q == p || errno)
587 die(1, "bad %s `%s'", what, p);
588 return (f);
589 }
590
591 int main(int argc, char *argv[])
592 {
593 int i;
594 opts o = { 0 };
595 const jobops *j;
596 struct timeval tv_next, tv_now;
597 double t, ttot;
598 unsigned n;
599 unsigned long ii;
600 clock_t c_start, c_stop;
601 double itot;
602 void *p;
603
604 ego(argv[0]);
605 o.t = 1;
606 for (;;) {
607 static const struct option opts[] = {
608 { "help", 0, 0, 'h' },
609 { "version", 0, 0, 'v' },
610 { "usage", 0, 0, 'u' },
611 { "list", 0, 0, 'l' },
612 { "name", OPTF_ARGREQ, 0, 'C' },
613 { "field-bits", OPTF_ARGREQ, 0, 'b' },
614 { "group-bits", OPTF_ARGREQ, 0, 'B' },
615 { "factors", OPTF_ARGREQ, 0, 'n' },
616 { "intervals", OPTF_ARGREQ, 0, 'i' },
617 { "time", OPTF_ARGREQ, 0, 't' },
618 { "no-check", 0, 0, 'q' },
619 { 0, 0, 0, 0 }
620 };
621
622 i = mdwopt(argc, argv, "hvulC:b:B:n:i:t:q", opts, 0, 0, 0);
623 if (i < 0) break;
624 switch (i) {
625 case 'h': help(stdout); exit(0);
626 case 'v': version(stdout); exit(0);
627 case 'u': usage(stdout); exit(0);
628 case 'l': exit(displaylists(listtab, argv + optind));
629 case 'C': o.name = optarg; break;
630 case 'b': o.fbits = uarg("field bits", optarg); break;
631 case 'B': o.gbits = uarg("subgroup bits", optarg); break;
632 case 'n': o.n = uarg("factor count", optarg); break;
633 case 'i': o.i = uarg("interval count", optarg); break;
634 case 't': o.t = farg("interval length", optarg); break;
635 case 'q': o.f |= OF_NOCHECK; break;
636 default: usage(stderr); exit(1);
637 }
638 }
639 if (optind + 1 != argc) { usage(stderr); exit(1); }
640
641 for (j = jobtab; j->name; j++)
642 if (strcmp(j->name, argv[optind]) == 0) break;
643 if (!j->name) die(1, "unknown job type `%s'", argv[optind]);
644 p = j->init(&o);
645
646 n = 0;
647 ttot = itot = 0;
648 gettimeofday(&tv_now, 0);
649 do {
650 tv_addl(&tv_next, &tv_now, o.t, fmod(o.t * MILLION, MILLION));
651 ii = 0;
652 c_start = clock();
653 do {
654 j->run(p);
655 ii++;
656 gettimeofday(&tv_now, 0);
657 } while (TV_CMP(&tv_now, <, &tv_next));
658 c_stop = clock();
659 t = (double)(c_stop - c_start)/CLOCKS_PER_SEC;
660 itot += ii;
661 ttot += t;
662 printf("%5u: did = %5lu; /sec = %5f; avg /sec = %5f\n",
663 n, ii, ii/t, itot/ttot);
664 fflush(stdout);
665 n++;
666 } while (!o.i || n < o.i);
667
668 return (0);
669 }
670
671 /*----- That's all, folks -------------------------------------------------*/