3 * Measure performance of various operations (Unix-specific)
5 * (c) 2004 Straylight/Edgeware
8 /*----- Licensing notice --------------------------------------------------*
10 * This file is part of Catacomb.
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.
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.
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,
28 /*----- Header files ------------------------------------------------------*/
30 #define _FILE_OFFSET_BITS 64
42 #include <sys/types.h>
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>
62 #include "mpbarrett.h"
81 /*----- Options -----------------------------------------------------------*/
84 const char *name
; /* Pre-configured named thing */
85 unsigned fbits
; /* Field size bits */
86 unsigned gbits
; /* Group size bits */
87 unsigned n
; /* Number of factors */
88 unsigned i
; /* Number of intervals (or zero) */
89 double t
; /* Time for each interval (secs) */
90 mp
*e
; /* Public exponent */
91 unsigned f
; /* Flags */
92 #define OF_NOCHECK 1u /* Don't do group checking */
95 /*----- Job switch --------------------------------------------------------*/
97 /* --- Barrett exponentiation --- */
99 typedef struct bar_ctx
{
105 static void *bar_init(opts
*o
)
107 bar_ctx
*c
= CREATE(bar_ctx
);
114 if (dh_parse(&qd
, &gp
))
115 die(1, "bad prime group: %s", qd
.e
);
117 if (!o
->fbits
) o
->fbits
= 1024;
118 dh_gen(&gp
, o
->gbits
, o
->fbits
, 0, &rand_global
, pgen_evspin
, 0);
120 mpbarrett_create(&c
->b
, gp
.p
);
123 c
->e
= xmalloc(c
->n
* sizeof(group_expfactor
));
124 for (i
= 0; i
< c
->n
; i
++) {
125 c
->e
[i
].base
= mprand_range(MP_NEW
, gp
.p
, &rand_global
, 0);
126 c
->e
[i
].exp
= mprand_range(MP_NEW
, gp
.q
, &rand_global
, 0);
132 static void bar_run(void *cc
)
135 mp
*d
= mpbarrett_exp(&c
->b
, MP_NEW
, c
->e
[0].base
, c
->e
[0].exp
);
139 static void barsim_run(void *cc
)
142 mp
*d
= mpbarrett_mexp(&c
->b
, MP_NEW
, c
->e
, c
->n
);
146 /* --- Montgomery exponentiation --- */
148 typedef struct mont_ctx
{
154 static void *mont_init(opts
*o
)
156 mont_ctx
*c
= CREATE(mont_ctx
);
163 if (dh_parse(&qd
, &gp
))
164 die(1, "bad prime group: %s", qd
.e
);
166 if (!o
->fbits
) o
->fbits
= 1024;
167 dh_gen(&gp
, o
->gbits
, o
->fbits
, 0, &rand_global
, pgen_evspin
, 0);
169 mpmont_create(&c
->m
, gp
.p
);
172 c
->e
= xmalloc(c
->n
* sizeof(mp_expfactor
));
173 for (i
= 0; i
< c
->n
; i
++) {
174 c
->e
[i
].base
= mprand_range(MP_NEW
, gp
.p
, &rand_global
, 0);
175 c
->e
[i
].exp
= mprand_range(MP_NEW
, gp
.q
, &rand_global
, 0);
181 static void mont_run(void *cc
)
184 mp
*d
= mpmont_expr(&c
->m
, MP_NEW
, c
->e
[0].base
, c
->e
[0].exp
);
188 static void montsim_run(void *cc
)
191 mp
*d
= mpmont_mexpr(&c
->m
, MP_NEW
, c
->e
, c
->n
);
195 /* --- Group exponentiation --- */
197 typedef struct gr_ctx
{
203 static void *grp_init(opts
*o
)
205 gr_ctx
*c
= CREATE(gr_ctx
);
213 if (dh_parse(&qd
, &gp
))
214 die(1, "bad prime group: %s", qd
.e
);
216 if (!o
->fbits
) o
->fbits
= 1024;
217 dh_gen(&gp
, o
->gbits
, o
->fbits
, 0, &rand_global
, pgen_evspin
, 0);
219 c
->g
= group_prime(&gp
);
220 if (!(o
->f
& OF_NOCHECK
) && (e
= G_CHECK(c
->g
, &rand_global
)) != 0)
221 die(1, "bad group: %s", e
);
224 c
->e
= xmalloc(c
->n
* sizeof(group_expfactor
));
225 for (i
= 0; i
< c
->n
; i
++) {
226 c
->e
[i
].base
= G_CREATE(c
->g
);
227 G_FROMINT(c
->g
, c
->e
[i
].base
,
228 mprand_range(MP_NEW
, gp
.p
, &rand_global
, 0));
229 c
->e
[i
].exp
= mprand_range(MP_NEW
, gp
.q
, &rand_global
, 0);
235 static void *grec_init(opts
*o
)
237 gr_ctx
*c
= CREATE(gr_ctx
);
244 die(1, "can't generate elliptic curves");
245 if ((e
= ec_getinfo(&ei
, o
->name
)) != 0)
246 die(1, "bad curve: %s", e
);
247 c
->g
= group_ec(&ei
);
248 if (!(o
->f
& OF_NOCHECK
) && (e
= G_CHECK(c
->g
, &rand_global
)) != 0)
249 die(1, "bad group: %s", e
);
252 c
->e
= xmalloc(c
->n
* sizeof(group_expfactor
));
253 for (i
= 0; i
< c
->n
; i
++) {
254 c
->e
[i
].base
= G_CREATE(c
->g
);
255 ec_rand(ei
.c
, &p
, &rand_global
);
256 G_FROMEC(c
->g
, c
->e
[i
].base
, &p
);
257 c
->e
[i
].exp
= mprand_range(MP_NEW
, ei
.r
, &rand_global
, 0);
263 static void gr_run(void *cc
)
266 ge
*x
= G_CREATE(c
->g
);
267 G_EXP(c
->g
, x
, c
->e
[0].base
, c
->e
[0].exp
);
271 static void grsim_run(void *cc
)
274 ge
*x
= G_CREATE(c
->g
);
275 G_MEXP(c
->g
, x
, c
->e
, c
->n
);
281 typedef struct x25519_jobctx
{
282 octet k
[X25519_KEYSZ
];
283 octet p
[X25519_PUBSZ
];
286 static void *x25519_jobinit(opts
*o
)
288 x25519_jobctx
*c
= CREATE(x25519_jobctx
);
289 rand_get(RAND_GLOBAL
, c
->k
, sizeof(c
->k
));
290 rand_get(RAND_GLOBAL
, c
->p
, sizeof(c
->p
));
294 static void x25519_jobrun(void *cc
)
295 { x25519_jobctx
*c
= cc
; octet z
[X25519_OUTSZ
]; x25519(z
, c
->k
, c
->p
); }
299 typedef struct x448_jobctx
{
304 static void *x448_jobinit(opts
*o
)
306 x448_jobctx
*c
= CREATE(x448_jobctx
);
307 rand_get(RAND_GLOBAL
, c
->k
, sizeof(c
->k
));
308 rand_get(RAND_GLOBAL
, c
->p
, sizeof(c
->p
));
312 static void x448_jobrun(void *cc
)
313 { x448_jobctx
*c
= cc
; octet z
[X448_OUTSZ
]; x448(z
, c
->k
, c
->p
); }
315 /* --- Ed25519 --- */
317 typedef struct ed25519_signctx
{
318 octet k
[ED25519_KEYSZ
];
319 octet K
[ED25519_PUBSZ
];
323 typedef struct ed25519_vrfctx
{
324 octet K
[ED25519_PUBSZ
];
326 octet sig
[ED25519_SIGSZ
];
329 static void *ed25519_signinit(opts
*o
)
331 ed25519_signctx
*c
= CREATE(ed25519_signctx
);
333 rand_get(RAND_GLOBAL
, c
->k
, sizeof(c
->k
));
334 rand_get(RAND_GLOBAL
, c
->m
, sizeof(c
->m
));
335 ed25519_pubkey(c
->K
, c
->k
, sizeof(c
->k
));
339 static void ed25519_signrun(void *cc
)
341 ed25519_signctx
*c
= cc
;
342 octet sig
[ED25519_SIGSZ
];
344 ed25519_sign(sig
, c
->k
, sizeof(c
->k
), c
->K
, c
->m
, sizeof(c
->m
));
347 static void *ed25519_vrfinit(opts
*o
)
349 octet k
[ED25519_KEYSZ
];
350 ed25519_vrfctx
*c
= CREATE(ed25519_vrfctx
);
352 rand_get(RAND_GLOBAL
, k
, sizeof(k
));
353 rand_get(RAND_GLOBAL
, c
->m
, sizeof(c
->m
));
354 ed25519_pubkey(c
->K
, k
, sizeof(k
));
355 ed25519_sign(c
->sig
, k
, sizeof(k
), c
->K
, c
->m
, sizeof(c
->m
));
359 static void ed25519_vrfrun(void *cc
)
361 ed25519_vrfctx
*c
= cc
;
362 ed25519_verify(c
->K
, c
->m
, sizeof(c
->m
), c
->sig
);
367 typedef struct ed448_signctx
{
368 octet k
[ED448_KEYSZ
];
369 octet K
[ED448_PUBSZ
];
373 typedef struct ed448_vrfctx
{
374 octet K
[ED448_PUBSZ
];
376 octet sig
[ED448_SIGSZ
];
379 static void *ed448_signinit(opts
*o
)
381 ed448_signctx
*c
= CREATE(ed448_signctx
);
383 rand_get(RAND_GLOBAL
, c
->k
, sizeof(c
->k
));
384 rand_get(RAND_GLOBAL
, c
->m
, sizeof(c
->m
));
385 ed448_pubkey(c
->K
, c
->k
, sizeof(c
->k
));
389 static void ed448_signrun(void *cc
)
391 ed448_signctx
*c
= cc
;
392 octet sig
[ED448_SIGSZ
];
394 ed448_sign(sig
, c
->k
, sizeof(c
->k
), c
->K
, 0, 0, 0, c
->m
, sizeof(c
->m
));
397 static void *ed448_vrfinit(opts
*o
)
399 octet k
[ED448_KEYSZ
];
400 ed448_vrfctx
*c
= CREATE(ed448_vrfctx
);
402 rand_get(RAND_GLOBAL
, k
, sizeof(k
));
403 rand_get(RAND_GLOBAL
, c
->m
, sizeof(c
->m
));
404 ed448_pubkey(c
->K
, k
, sizeof(k
));
405 ed448_sign(c
->sig
, k
, sizeof(k
), c
->K
, 0, 0, 0, c
->m
, sizeof(c
->m
));
409 static void ed448_vrfrun(void *cc
)
411 ed448_vrfctx
*c
= cc
;
412 ed448_verify(c
->K
, 0, 0, 0, c
->m
, sizeof(c
->m
), c
->sig
);
417 typedef struct rsapriv_ctx
{
423 static void *rsapriv_init(opts
*o
)
425 rsapriv_ctx
*c
= CREATE(rsapriv_ctx
);
427 if (!o
->fbits
) o
->fbits
= 1024;
428 if (!o
->e
) o
->e
= mp_fromulong(MP_NEW
, 65537);
429 rsa_gen_e(&c
->rp
, o
->fbits
, o
->e
, &rand_global
, 0, pgen_evspin
, 0);
430 rsa_privcreate(&c
->rpc
, &c
->rp
, 0);
431 c
->m
= mprand_range(MP_NEW
, c
->rp
.n
, &rand_global
, 0);
435 static void *rsaprivblind_init(opts
*o
)
437 rsapriv_ctx
*c
= CREATE(rsapriv_ctx
);
439 if (!o
->fbits
) o
->fbits
= 1024;
440 if (!o
->e
) o
->e
= mp_fromulong(MP_NEW
, 65537);
441 rsa_gen_e(&c
->rp
, o
->fbits
, o
->e
, &rand_global
, 0, pgen_evspin
, 0);
442 rsa_privcreate(&c
->rpc
, &c
->rp
, fibrand_create(0));
443 c
->m
= mprand_range(MP_NEW
, c
->rp
.n
, &rand_global
, 0);
447 static void rsapriv_run(void *cc
)
450 mp
*d
= rsa_privop(&c
->rpc
, MP_NEW
, c
->m
);
454 typedef struct rsapub_ctx
{
460 static void *rsapub_init(opts
*o
)
462 rsapub_ctx
*c
= CREATE(rsapub_ctx
);
465 if (!o
->fbits
) o
->fbits
= 1024;
466 if (!o
->e
) o
->e
= mp_fromulong(MP_NEW
, 65537);
467 rsa_gen_e(&rp
, o
->fbits
, o
->e
, &rand_global
, 0, pgen_evspin
, 0);
468 c
->rp
.n
= MP_COPY(rp
.n
);
469 c
->rp
.e
= MP_COPY(rp
.e
);
471 rsa_pubcreate(&c
->rpc
, &c
->rp
);
472 c
->m
= mprand_range(MP_NEW
, c
->rp
.n
, &rand_global
, 0);
476 static void rsapub_run(void *cc
)
479 mp
*d
= rsa_pubop(&c
->rpc
, MP_NEW
, c
->m
);
483 /* --- Symmetric encryption --- */
485 typedef struct ksched_ctx
{
491 static void *ksched_init(opts
*o
)
493 ksched_ctx
*c
= CREATE(ksched_ctx
);
495 die(1, "must specify encryption scheme name");
496 if ((c
->c
= gcipher_byname(o
->name
)) == 0)
497 die(1, "encryption scheme `%s' not known", o
->name
);
498 c
->ksz
= keysz(o
->gbits
/8, c
->c
->keysz
);
499 c
->k
= xmalloc(c
->ksz
);
500 rand_get(RAND_GLOBAL
, c
->k
, c
->ksz
);
504 static void ksched_run(void *cc
)
507 gcipher
*gc
= GC_INIT(c
->c
, c
->k
, c
->ksz
);
511 typedef struct enc_ctx
{
518 static void *enc_init(opts
*o
)
520 enc_ctx
*c
= CREATE(enc_ctx
);
525 die(1, "must specify encryption scheme name");
526 if ((cc
= gcipher_byname(o
->name
)) == 0)
527 die(1, "encryption scheme `%s' not known", o
->name
);
528 ksz
= keysz(0, cc
->keysz
);
530 rand_get(RAND_GLOBAL
, k
, ksz
);
531 c
->c
= GC_INIT(cc
, k
, ksz
);
533 c
->sz
= o
->gbits ? o
->gbits
: 65536;
534 c
->n
= o
->n ? o
->n
: 16;
535 c
->m
= xmalloc(c
->sz
);
539 static void enc_run(void *cc
)
543 for (i
= 0; i
< c
->n
; i
++)
544 GC_ENCRYPT(c
->c
, c
->m
, c
->m
, c
->sz
);
547 /* --- Hashing --- */
549 typedef struct hash_ctx
{
556 static void *hash_init(opts
*o
)
558 hash_ctx
*c
= CREATE(hash_ctx
);
560 die(1, "must specify hash function name");
561 if ((c
->h
= ghash_byname(o
->name
)) == 0)
562 die(1, "hash function `%s' not known", o
->name
);
563 c
->sz
= o
->gbits ? o
->gbits
: 65536;
564 c
->n
= o
->n ? o
->n
: 16;
565 c
->m
= xmalloc(c
->sz
);
569 static void hash_run(void *cc
)
573 ghash
*h
= GH_INIT(c
->h
);
574 for (i
= 0; i
< c
->n
; i
++)
575 GH_HASH(h
, c
->m
, c
->sz
);
580 /* --- Poly1305 --- */
582 typedef struct poly1305_jobctx
{
584 octet s
[POLY1305_MASKSZ
];
590 static void *poly1305_jobinit(opts
*o
)
592 octet k
[POLY1305_KEYSZ
];
593 poly1305_jobctx
*c
= CREATE(poly1305_jobctx
);
594 rand_get(RAND_GLOBAL
, k
, sizeof(k
));
595 poly1305_keyinit(&c
->k
, k
, sizeof(k
));
596 rand_get(RAND_GLOBAL
, c
->s
, sizeof(c
->s
));
597 c
->sz
= o
->gbits ? o
->gbits
: 65536;
598 c
->n
= o
->n ? o
->n
: 16;
599 c
->m
= xmalloc(c
->sz
);
603 static void poly1305_jobrun(void *cc
)
605 poly1305_jobctx
*c
= cc
;
607 octet t
[POLY1305_TAGSZ
];
609 poly1305_macinit(&ctx
, &c
->k
, c
->s
);
610 for (i
= 0; i
< c
->n
; i
++) poly1305_hash(&ctx
, c
->m
, c
->sz
);
611 poly1305_done(&ctx
, t
);
614 /* --- Job table --- */
616 typedef struct jobops
{
618 void *(*init
)(opts
*);
622 static const jobops jobtab
[] = {
623 { "g-prime-exp", grp_init
, gr_run
},
624 { "g-ec-mul", grec_init
, gr_run
},
625 { "g-prime-exp-sim", grp_init
, grsim_run
},
626 { "g-ec-mul-sim", grec_init
, grsim_run
},
627 { "barrett-exp", bar_init
, bar_run
},
628 { "barrett-exp-sim", bar_init
, barsim_run
},
629 { "mont-exp", mont_init
, mont_run
},
630 { "mont-exp-sim", mont_init
, montsim_run
},
631 { "rsa-priv", rsapriv_init
, rsapriv_run
},
632 { "rsa-priv-blind", rsaprivblind_init
, rsapriv_run
},
633 { "rsa-pub", rsapub_init
, rsapub_run
},
634 { "x25519", x25519_jobinit
, x25519_jobrun
},
635 { "x448", x448_jobinit
, x448_jobrun
},
636 { "ed25519-sign", ed25519_signinit
, ed25519_signrun
},
637 { "ed25519-vrf", ed25519_vrfinit
, ed25519_vrfrun
},
638 { "ed448-sign", ed448_signinit
, ed448_signrun
},
639 { "ed448-vrf", ed448_vrfinit
, ed448_vrfrun
},
640 { "ksched", ksched_init
, ksched_run
},
641 { "enc", enc_init
, enc_run
},
642 { "hash", hash_init
, hash_run
},
643 { "poly1305", poly1305_jobinit
, poly1305_jobrun
},
647 /*----- Main code ---------------------------------------------------------*/
649 void version(FILE *fp
)
651 pquis(fp
, "$, Catacomb " VERSION
"\n");
654 static void usage(FILE *fp
)
656 pquis(fp
, "Usage: $ [-options] job\n");
659 static void help(FILE *fp
)
665 Various performance tests.\n\
669 -h, --help Show this help text.\n\
670 -v, --version Show program version number.\n\
671 -u, --usage Show terse usage message.\n\
672 -l, --list [ITEM...] List all the various names of things.\n\
674 -C, --name=NAME Select curve/DH-group/enc/hash name.\n\
675 -b, --field-bits Field size for g-prime and rsa.\n\
676 -q, --no-check Don't check field/group for validity.\n\
677 -B, --group-bits Group size for g-prime; key size for ksched;\n\
678 data size for enc and hash.\n\
679 -n, --factors=COUNT Number of factors for {exp,mul}-sim.\n\
680 -i, --intervals=COUNT Number of intervals to run for. [0; forever]\n\
681 -t, --time=TIME Length of an interval in seconds. [1]\n\
687 listtab[i].name, listtab[i].name) \
689 jobtab[i].name, jobtab[i].name) \
690 LI("Elliptic curves", ec, \
691 ectab[i].name, ectab[i].name) \
692 LI("Diffie-Hellman groups", dh, \
693 ptab[i].name, ptab[i].name) \
694 LI("Encryption algorithms", cipher, \
695 gciphertab[i], gciphertab[i]->name) \
696 LI("Hash functions", hash, \
697 ghashtab[i], ghashtab[i]->name)
699 MAKELISTTAB(listtab
, LISTS
)
701 static unsigned uarg(const char *what
, const char *p
)
706 u
= strtoul(p
, &q
, 0);
707 if (*q
|| u
> UINT_MAX
|| q
== p
|| errno
)
708 die(1, "bad %s `%s'", what
, p
);
712 static mp
*mparg(const char *what
, const char *p
)
715 mp
*x
= mp_readstring(MP_NEW
, p
, &q
, 0);
716 if (!x
|| *q
) die(1, "bad %s `%s'", what
, p
);
720 static double farg(const char *what
, const char *p
)
726 if (*q
|| q
== p
|| errno
)
727 die(1, "bad %s `%s'", what
, p
);
731 int main(int argc
, char *argv
[])
736 struct timeval tv_next
, tv_now
;
740 clock_t c_start
, c_stop
;
747 static const struct option opts
[] = {
748 { "help", 0, 0, 'h' },
749 { "version", 0, 0, 'v' },
750 { "usage", 0, 0, 'u' },
751 { "list", 0, 0, 'l' },
752 { "name", OPTF_ARGREQ
, 0, 'C' },
753 { "field-bits", OPTF_ARGREQ
, 0, 'b' },
754 { "group-bits", OPTF_ARGREQ
, 0, 'B' },
755 { "factors", OPTF_ARGREQ
, 0, 'n' },
756 { "intervals", OPTF_ARGREQ
, 0, 'i' },
757 { "public-exponent", OPTF_ARGREQ
, 0, 'e' },
758 { "time", OPTF_ARGREQ
, 0, 't' },
759 { "no-check", 0, 0, 'q' },
763 i
= mdwopt(argc
, argv
, "hvulC:b:B:n:i:e:t:q", opts
, 0, 0, 0);
766 case 'h': help(stdout
); exit(0);
767 case 'v': version(stdout
); exit(0);
768 case 'u': usage(stdout
); exit(0);
769 case 'l': exit(displaylists(listtab
, argv
+ optind
));
770 case 'C': o
.name
= optarg
; break;
771 case 'b': o
.fbits
= uarg("field bits", optarg
); break;
772 case 'B': o
.gbits
= uarg("subgroup bits", optarg
); break;
773 case 'n': o
.n
= uarg("factor count", optarg
); break;
775 mp_drop(o
.e
); o
.e
= mparg("public exponent", optarg
);
776 if (MP_CMP(o
.e
, <, MP_THREE
) || MP_EVENP(o
.e
))
777 die(1, "invalid public exponent");
779 case 'i': o
.i
= uarg("interval count", optarg
); break;
780 case 't': o
.t
= farg("interval length", optarg
); break;
781 case 'q': o
.f
|= OF_NOCHECK
; break;
782 default: usage(stderr
); exit(1);
785 if (optind
+ 1 != argc
) { usage(stderr
); exit(1); }
787 for (j
= jobtab
; j
->name
; j
++)
788 if (strcmp(j
->name
, argv
[optind
]) == 0) break;
789 if (!j
->name
) die(1, "unknown job type `%s'", argv
[optind
]);
794 gettimeofday(&tv_now
, 0);
796 tv_addl(&tv_next
, &tv_now
, o
.t
, fmod(o
.t
* MILLION
, MILLION
));
802 gettimeofday(&tv_now
, 0);
803 } while (TV_CMP(&tv_now
, <, &tv_next
));
805 t
= (double)(c_stop
- c_start
)/CLOCKS_PER_SEC
;
808 printf("%5u: did = %5lu; /sec = %5f; avg /sec = %5f\n",
809 n
, ii
, ii
/t
, itot
/ttot
);
812 } while (!o
.i
|| n
< o
.i
);
817 /*----- That's all, folks -------------------------------------------------*/