ed25519-donna implementation
[mkp224o.git] / main.c
blob95d444e1f9b753d3af71da74c47e71614fcbfa43
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4 #include <string.h>
5 #include <assert.h>
6 #include <time.h>
7 #include <pthread.h>
8 #include <signal.h>
9 #include <sys/stat.h>
10 #include <sodium/randombytes.h>
12 #include "types.h"
13 #include "likely.h"
14 #include "vec.h"
15 #include "base32.h"
16 #include "keccak.h"
17 #include "ed25519/ed25519.h"
19 // additional leading zero is added by C
20 static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
21 #define pkprefixlen (29 + 3)
22 static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
23 #define skprefixlen (29 + 3)
24 static const char * const checksumstr = ".onion checksum";
25 #define checksumstrlen 15
27 // output directory
28 static char *workdir = 0;
29 static size_t workdirlen = 0;
31 static int quietflag = 0;
33 #define SECRET_LEN 64
34 #define PUBLIC_LEN 32
35 #define SEED_LEN 32
36 // with checksum + version num
37 #define PUBONION_LEN (PUBLIC_LEN + 3)
38 // with newline included
39 #define ONIONLEN 62
41 static size_t onionendpos; // end of .onion within string
42 static size_t direndpos; // end of dir before .onion within string
43 static size_t printstartpos; // where to start printing from
44 static size_t printlen; // precalculated, related to printstartpos
46 static pthread_mutex_t fout_mutex;
47 static FILE *fout;
48 static size_t numneedgenerate = 0;
49 static pthread_mutex_t keysgenerated_mutex;
50 static volatile size_t keysgenerated = 0;
51 static volatile int endwork = 0;
53 static void termhandler(int sig)
55 switch (sig) {
56 case SIGTERM:
57 case SIGINT:
58 endwork = 1;
59 break;
63 // filters stuff
65 struct binfilter {
66 u8 f[PUBLIC_LEN];
67 size_t len; // real len minus one
68 u8 mask;
69 } ;
71 #ifdef INTFILTER
72 struct intfilter {
73 u64 f,m;
74 } ;
75 VEC_STRUCT(ifiltervec,struct intfilter) ifilters;
76 #else
77 VEC_STRUCT(bfiltervec,struct binfilter) bfilters;
78 #endif
80 static void filters_init()
82 #ifdef INTFILTER
83 VEC_INIT(ifilters);
84 #else
85 VEC_INIT(bfilters);
86 #endif
89 static void filters_add(const char *filter)
91 struct binfilter bf;
92 size_t ret, ret2;
93 #ifdef INTFILTER
94 union intconv {
95 u64 i;
96 u8 b[8];
97 } fc,mc;
98 #endif
100 if (!base32_valid(filter,&ret)) {
101 fprintf(stderr, "filter \"%s\" is invalid\n", filter);
102 return;
104 ret = BASE32_FROM_LEN(ret);
105 if (!ret)
106 return;
107 #ifdef INTFILTER
108 if (ret > 8)
109 #else
110 if (ret > PUBLIC_LEN)
111 #endif
113 fprintf(stderr, "filter \"%s\" is too long\n", filter);
114 return;
116 ret2 = base32_from(bf.f,&bf.mask,filter);
117 assert(ret == ret2);
118 //printf("--m:%02X\n", bf.mask);
119 bf.len = ret - 1;
120 #ifdef INTFILTER
121 mc.i = 0;
122 for (size_t i = 0;i < bf.len;++i)
123 mc.b[i] = 0xFF;
124 mc.b[bf.len] = bf.mask;
125 memcpy(fc.b,bf.f,8);
126 fc.i &= mc.i;
127 struct intfilter ifltr = {fc.i,mc.i};
128 VEC_ADD(ifilters,ifltr);
129 #else
130 VEC_ADD(bfilters,bf);
131 #endif
134 static void filters_clean()
136 #ifdef INTFILTER
137 VEC_FREE(ifilters);
138 #else
139 VEC_FREE(bfilters);
140 #endif
143 static size_t filters_count()
145 #ifdef INTFILTER
146 return VEC_LENGTH(ifilters);
147 #else
148 return VEC_LENGTH(bfilters);
149 #endif
152 #ifdef INTFILTER
154 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
155 #define MATCHFILTER(it,pk) ((*(u64 *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
157 #else
159 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
160 #define MATCHFILTER(it,pk) ( \
161 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
162 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
164 #endif
166 static void loadfilterfile(const char *fname)
168 char buf[128];
169 FILE *f = fopen(fname,"r");
170 while (fgets(buf,sizeof(buf),f)) {
171 for (char *p = buf;*p;++p) {
172 if(*p == '\n') {
173 *p = 0;
174 break;
177 if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0)
178 filters_add(buf);
182 static void printfilters()
184 size_t i,l;
185 #ifdef INTFILTER
186 l = VEC_LENGTH(ifilters);
187 #else
188 l = VEC_LENGTH(bfilters);
189 #endif
190 if (l)
191 fprintf(stderr, "filters:\n");
192 else
193 fprintf(stderr, "no filters defined\n");
195 for (i = 0;i < l;++i) {
196 char buf0[256],buf1[256];
197 u8 bufx[128];
198 #ifdef INTFILTER
199 size_t len = 0;
200 u8 *imraw = (u8 *)&VEC_BUF(ifilters,i).m;
201 while (len < 8 && imraw[len] != 0x00) ++len;
202 u8 mask = imraw[len-1];
203 u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f;
204 #else
205 size_t len = VEC_BUF(bfilters,i).len + 1;
206 u8 mask = VEC_BUF(bfilters,i).mask;
207 u8 *ifraw = VEC_BUF(bfilters,i).f;
208 #endif
209 base32_to(buf0,ifraw,len);
210 memcpy(bufx,ifraw,len);
211 bufx[len - 1] |= ~mask;
212 base32_to(buf1,bufx,len);
213 char *a = buf0,*b = buf1;
214 while (*a && *a == *b)
215 ++a, ++b;
216 *a = 0;
217 fprintf(stderr, "\t%s\n",buf0);
221 // statistics, if enabled
222 #ifdef STATISTICS
223 struct statstruct {
224 union {
225 u32 numcalc;
226 size_t align_numcalc;
228 union {
229 u32 numsuccess;
230 size_t align_numsuccess;
233 VEC_STRUCT(statsvec,struct statstruct);
235 struct tstatstruct {
236 u64 numcalc;
237 u64 numsuccess;
238 u32 oldnumcalc;
239 u32 oldnumsuccess;
241 VEC_STRUCT(tstatsvec,struct tstatstruct);
242 #endif
245 static void onionready(char *sname, const u8 *secret, const u8 *pubonion)
247 FILE *fh;
249 if (endwork)
250 return;
252 if (numneedgenerate) {
253 pthread_mutex_lock(&keysgenerated_mutex);
254 if (keysgenerated >= numneedgenerate) {
255 endwork = 1;
256 pthread_mutex_unlock(&keysgenerated_mutex);
257 return;
261 if (mkdir(sname,0700) != 0) {
262 if (numneedgenerate)
263 pthread_mutex_unlock(&keysgenerated_mutex);
264 return;
267 if (numneedgenerate) {
268 ++keysgenerated;
269 if (keysgenerated >= numneedgenerate)
270 endwork = 1;
271 pthread_mutex_unlock(&keysgenerated_mutex);
274 strcpy(&sname[onionendpos], "/hs_ed25519_secret_key");
275 fh = fopen(sname, "wb");
276 if (fh) {
277 fwrite(secret, skprefixlen + SECRET_LEN, 1, fh);
278 fclose(fh);
281 strcpy(&sname[onionendpos], "/hostname");
282 fh = fopen(sname, "w");
283 if (fh) {
284 sname[onionendpos] = '\n';
285 fwrite(&sname[direndpos], ONIONLEN+1, 1, fh);
286 fclose(fh);
289 strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
290 fh = fopen(sname, "wb");
291 if (fh) {
292 fwrite(pubonion, pkprefixlen + PUBLIC_LEN, 1, fh);
293 fclose(fh);
296 if (fout) {
297 sname[onionendpos] = '\n';
298 pthread_mutex_lock(&fout_mutex);
299 fwrite(&sname[printstartpos], printlen, 1, fout);
300 fflush(fout);
301 pthread_mutex_unlock(&fout_mutex);
305 // little endian inc
306 static void addseed(u8 *seed)
308 register unsigned int c = 1;
309 for (size_t i = 0;i < SEED_LEN;++i) {
310 c = (unsigned int)seed[i] + c; seed[i] = c & 0xFF; c >>= 8;
311 // unsure if needed
312 if (!c) break;
316 static void *dowork(void *task)
318 union pubonionunion {
319 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
320 struct {
321 u64 prefix[4];
322 u64 key[4];
323 u64 hash[4];
325 } pubonion;
326 u8 * const pk = &pubonion.raw[pkprefixlen];
327 u8 secret[skprefixlen + SECRET_LEN];
328 u8 * const sk = &secret[skprefixlen];
329 u8 seed[SEED_LEN];
330 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
331 size_t i;
332 char *sname;
333 #ifdef STATISTICS
334 struct statstruct *st = (struct statstruct *)task;
335 #endif
337 memcpy(secret,skprefix,skprefixlen);
338 memcpy(pubonion.raw,pkprefix,pkprefixlen);
339 // write version later as it will be overwritten by hash
340 memcpy(hashsrc,checksumstr,checksumstrlen);
341 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
343 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
344 if (workdir)
345 memcpy(sname,workdir,workdirlen);
347 initseed:
348 randombytes(seed,sizeof(seed));
350 again:
351 if (unlikely(endwork))
352 goto end;
354 ed25519_seckey_expand(sk,seed);
355 ed25519_pubkey(pk,sk);
357 #ifdef STATISTICS
358 ++st->numcalc;
359 #endif
361 FILTERFOR(i) {
362 if (unlikely(MATCHFILTER(i,pk))) {
363 #ifdef STATISTICS
364 ++st->numsuccess;
365 #endif
366 // calc checksum
367 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
368 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
369 // version byte
370 pk[PUBLIC_LEN + 2] = 0x03;
371 // base32
372 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
373 onionready(sname, secret, pubonion.raw);
374 goto initseed;
377 addseed(seed);
378 goto again;
380 end:
381 free(sname);
382 return 0;
385 static void addu64toscalar32(u8 *dst,u64 v)
387 int i;
388 u32 c = 0;
389 for (i = 0;i < 32;++i) {
390 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
391 v >>= 8;
392 ++dst;
396 static void *dofastwork(void *task)
398 union pubonionunion {
399 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
400 struct {
401 u64 prefix[4];
402 u64 key[4];
403 u64 hash[4];
405 } pubonion;
406 u8 * const pk = &pubonion.raw[pkprefixlen];
407 u8 secret[skprefixlen + SECRET_LEN];
408 u8 * const sk = &secret[skprefixlen];
409 u8 seed[SEED_LEN];
410 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
411 ge_p3 ge_public;
412 u64 counter;
413 size_t i;
414 char *sname;
415 #ifdef STATISTICS
416 struct statstruct *st = (struct statstruct *)task;
417 #endif
419 memcpy(secret, skprefix, skprefixlen);
420 memcpy(pubonion.raw, pkprefix, pkprefixlen);
421 // write version later as it will be overwritten by hash
422 memcpy(hashsrc, checksumstr, checksumstrlen);
423 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
425 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
426 if (workdir)
427 memcpy(sname, workdir, workdirlen);
429 initseed:
430 randombytes(seed,sizeof(seed));
431 ed25519_seckey_expand(sk,seed);
433 ge_scalarmult_base(&ge_public,sk);
434 ge_p3_tobytes(pk,&ge_public);
436 for (counter = 0;counter < U64_MAX-8;counter += 8) {
437 ge_p1p1 sum;
439 if (unlikely(endwork))
440 goto end;
442 FILTERFOR(i) {
443 if (unlikely(MATCHFILTER(i,pk))) {
444 // found!
445 // update secret key with counter
446 addu64toscalar32(sk,counter);
447 // sanity check
448 if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
449 /* These operations should be a no-op. */
450 sk[0] &= 248;
451 sk[31] &= 63;
452 sk[31] |= 64;
454 else goto initseed;
455 #ifdef STATISTICS
456 ++st->numsuccess;
457 #endif
458 // calc checksum
459 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
460 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
461 // version byte
462 pk[PUBLIC_LEN + 2] = 0x03;
463 // full name
464 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
465 onionready(sname,secret,pubonion.raw);
466 // don't reuse same seed
467 goto initseed;
471 // next
472 ge_add(&sum, &ge_public,&ge_eightpoint);
473 ge_p1p1_to_p3(&ge_public,&sum);
474 ge_p3_tobytes(pk,&ge_public);
475 #ifdef STATISTICS
476 ++st->numcalc;
477 #endif
479 goto initseed;
481 end:
482 free(sname);
483 return 0;
486 void printhelp(const char *progname)
488 fprintf(stderr,
489 "Usage: %s filter [filter...] [options]\n"
490 " %s -f filterfile [options]\n"
491 "Options:\n"
492 "\t-h - print help\n"
493 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
494 "\t-q - do not print diagnostic output to stderr\n"
495 "\t-x - do not print onion names\n"
496 "\t-o filename - output onion names to specified file\n"
497 "\t-F - include directory names in onion names output\n"
498 "\t-d dirname - output directory\n"
499 "\t-t numthreads - specify number of threads (default - auto)\n"
500 "\t-j numthreads - same as -t\n"
501 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
502 "\t-z - use faster key generation method. this is now default\n"
503 "\t-Z - use slower key generation method\n"
504 "\t-s - print statistics each 10 seconds\n"
505 "\t-S t - print statistics every specified ammount of seconds\n"
506 "\t-T - do not reset statistics counters when printing\n"
507 ,progname,progname);
508 exit(1);
511 void setworkdir(const char *wd)
513 free(workdir);
514 size_t l = strlen(wd);
515 if (!l) {
516 workdir = 0;
517 workdirlen = 0;
518 if (!quietflag)
519 fprintf(stderr, "unset workdir\n");
520 return;
522 int needslash = 0;
523 if (wd[l-1] != '/')
524 needslash = 1;
525 char *s = malloc(l + needslash + 1);
526 memcpy(s, wd, l);
527 if (needslash)
528 s[l++] = '/';
529 s[l] = 0;
531 workdir = s;
532 workdirlen = l;
533 if (!quietflag)
534 fprintf(stderr,"set workdir: %s\n",workdir);
537 VEC_STRUCT(threadvec, pthread_t);
539 int main(int argc,char **argv)
541 char *outfile = 0;
542 const char *arg;
543 int ignoreargs = 0;
544 int dirnameflag = 0;
545 int numthreads = 0;
546 int fastkeygen = 1;
547 struct threadvec threads;
548 #ifdef STATISTICS
549 struct statsvec stats;
550 struct tstatsvec tstats;
551 u64 reportdelay = 0;
552 int realtimestats = 1;
553 #endif
554 int tret;
556 ge_initeightpoint();
557 filters_init();
559 fout = stdout;
560 pthread_mutex_init(&keysgenerated_mutex, 0);
561 pthread_mutex_init(&fout_mutex, 0);
563 const char *progname = argv[0];
564 if (argc <= 1)
565 printhelp(progname);
566 argc--, argv++;
568 while (argc--) {
569 arg = *argv++;
570 if (!ignoreargs && *arg == '-') {
571 int numargit = 0;
572 nextarg:
573 ++arg;
574 ++numargit;
575 if (*arg == '-') {
576 if (numargit > 1) {
577 fprintf(stderr, "unrecognised argument: -\n");
578 exit(1);
580 ++arg;
581 if (!*arg)
582 ignoreargs = 1;
583 else if (!strcmp(arg, "help"))
584 printhelp(progname);
585 else {
586 fprintf(stderr, "unrecognised argument: --%s\n", arg);
587 exit(1);
589 numargit = 0;
591 else if (*arg == 0) {
592 if (numargit == 1)
593 ignoreargs = 1;
594 continue;
596 else if (*arg == 'h')
597 printhelp(progname);
598 else if (*arg == 'f') {
599 if (argc--)
600 loadfilterfile(*argv++);
601 else {
602 fprintf(stderr, "additional argument required\n");
603 exit(1);
606 else if (*arg == 'q')
607 ++quietflag;
608 else if (*arg == 'x')
609 fout = 0;
610 else if (*arg == 'o') {
611 if (argc--)
612 outfile = *argv++;
613 else {
614 fprintf(stderr, "additional argument required\n");
615 exit(1);
618 else if (*arg == 'F')
619 dirnameflag = 1;
620 else if (*arg == 'd') {
621 if (argc--) {
622 setworkdir(*argv++);
624 else {
625 fprintf(stderr, "additional argument required\n");
628 else if (*arg == 't' || *arg == 'j') {
629 if (argc--)
630 numthreads = atoi(*argv++);
631 else {
632 fprintf(stderr, "additional argument required\n");
633 exit(1);
636 else if (*arg == 'n') {
637 if (argc--)
638 numneedgenerate = (size_t)atoll(*argv++);
639 else {
640 fprintf(stderr, "additional argument required\n");
641 exit(1);
644 else if (*arg == 'Z')
645 fastkeygen = 0;
646 else if (*arg == 'z')
647 fastkeygen = 1;
648 else if (*arg == 's') {
649 #ifdef STATISTICS
650 reportdelay = 10000000;
651 #else
652 fprintf(stderr,"statistics support not compiled in\n");
653 exit(1);
654 #endif
656 else if (*arg == 'S') {
657 #ifdef STATISTICS
658 if (argc--)
659 reportdelay = (u64)atoll(*argv++) * 1000000;
660 else {
661 fprintf(stderr, "additional argument required\n");
662 exit(1);
664 #else
665 fprintf(stderr,"statistics support not compiled in\n");
666 exit(1);
667 #endif
669 else if (*arg == 'T') {
670 #ifdef STATISTICS
671 realtimestats = 0;
672 #else
673 fprintf(stderr,"statistics support not compiled in\n");
674 exit(1);
675 #endif
677 else {
678 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
679 exit(1);
681 if (numargit)
682 goto nextarg;
684 else filters_add(arg);
687 if (!quietflag)
688 printfilters();
690 if (!filters_count())
691 return 0;
693 if (outfile)
694 fout = fopen(outfile, "w");
696 if (workdir)
697 mkdir(workdir, 0700);
699 direndpos = workdirlen;
700 onionendpos = workdirlen + ONIONLEN;
702 if (!dirnameflag) {
703 printstartpos = direndpos;
704 printlen = ONIONLEN + 1;
705 } else {
706 printstartpos = 0;
707 printlen = onionendpos + 1;
710 if (numthreads <= 0) {
711 // TODO: autodetect
712 numthreads = 1;
715 signal(SIGTERM,termhandler);
716 signal(SIGINT,termhandler);
718 VEC_INIT(threads);
719 VEC_ADDN(threads,pthread_t,numthreads);
720 #ifdef STATISTICS
721 VEC_INIT(stats);
722 VEC_INIT(tstats);
723 VEC_ADDN(stats,struct statstruct,numthreads);
724 VEC_ADDN(tstats,struct tstatstruct,numthreads);
725 memset(&VEC_BUF(stats,0),0,sizeof(struct statstruct) * VEC_LENGTH(stats));
726 memset(&VEC_BUF(tstats,0),0,sizeof(struct tstatstruct) * VEC_LENGTH(tstats));
727 #endif
729 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
730 void *tp = 0;
731 #ifdef STATISTICS
732 tp = &VEC_BUF(stats,i);
733 #endif
734 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
735 if (tret) {
736 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
737 exit(1);
741 #ifdef STATISTICS
742 struct timespec nowtime;
743 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
744 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
745 fprintf(stderr, "failed to get time\n");
746 exit(1);
748 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
749 #endif
750 struct timespec ts;
751 memset(&ts,0,sizeof(ts));
752 ts.tv_nsec = 100000000;
753 while (!endwork) {
754 if (numneedgenerate && keysgenerated >= numneedgenerate) {
755 endwork = 1;
756 break;
758 nanosleep(&ts,0);
760 #ifdef STATISTICS
761 clock_gettime(CLOCK_MONOTONIC,&nowtime);
762 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
763 u64 sumcalc = 0,sumsuccess = 0;
764 for (size_t i = 0;i < numthreads;++i) {
765 u32 newt,tdiff;
766 // numcalc
767 newt = VEC_BUF(stats,i).numcalc;
768 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
769 VEC_BUF(tstats,i).oldnumcalc = newt;
770 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
771 sumcalc += VEC_BUF(tstats,i).numcalc;
772 // numsuccess
773 newt = VEC_BUF(stats,i).numsuccess;
774 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
775 VEC_BUF(tstats,i).oldnumsuccess = newt;
776 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
777 sumsuccess += VEC_BUF(tstats,i).numsuccess;
779 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
780 if (ireporttime)
781 ireporttime += reportdelay;
782 else
783 ireporttime = inowtime;
784 if (!ireporttime)
785 ireporttime = 1;
787 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
788 double successpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
789 fprintf(stderr,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec,successpersec,(inowtime - istarttime + elapsedoffset) / 1000000.0);
791 if (realtimestats) {
792 for (size_t i = 0;i < numthreads;++i) {
793 VEC_BUF(tstats,i).numcalc = 0;
794 VEC_BUF(tstats,i).numsuccess = 0;
796 elapsedoffset += inowtime - istarttime;
797 istarttime = inowtime;
800 if (sumcalc > U64_MAX / 2) {
801 for (size_t i = 0;i < numthreads;++i) {
802 VEC_BUF(tstats,i).numcalc /= 2;
803 VEC_BUF(tstats,i).numsuccess /= 2;
805 u64 timediff = (inowtime - istarttime + 1) / 2;
806 elapsedoffset += timediff;
807 istarttime += timediff;
809 #endif
812 if (!quietflag)
813 fprintf(stderr, "waiting for threads to finish...\n");
814 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
815 pthread_join(VEC_BUF(threads,i),0);
816 if (!quietflag)
817 fprintf(stderr, "done, quitting\n");
819 pthread_mutex_destroy(&keysgenerated_mutex);
820 pthread_mutex_destroy(&fout_mutex);
821 filters_clean();
823 if (outfile)
824 fclose(fout);
826 return 0;