wip fixes
[mkp224o.git] / main.c
blobb1f857705224f53a72b5f00c93883e90a372d151
1 #ifdef __linux__
2 #define _POSIX_C_SOURCE 200112L
3 #endif
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <time.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <sys/stat.h>
14 #include <sodium/randombytes.h>
16 #include "types.h"
17 #include "likely.h"
18 #include "vec.h"
19 #include "base32.h"
20 #include "cpucount.h"
21 #include "keccak.h"
22 #include "ed25519/ed25519.h"
24 // additional leading zero is added by C
25 static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
26 #define pkprefixlen (29 + 3)
27 static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
28 #define skprefixlen (29 + 3)
29 static const char * const checksumstr = ".onion checksum";
30 #define checksumstrlen 15
32 // output directory
33 static char *workdir = 0;
34 static size_t workdirlen = 0;
36 static int quietflag = 0;
38 #define SECRET_LEN 64
39 #define PUBLIC_LEN 32
40 #define SEED_LEN 32
41 // with checksum + version num
42 #define PUBONION_LEN (PUBLIC_LEN + 3)
43 // with newline included
44 #define ONIONLEN 62
46 static size_t onionendpos; // end of .onion within string
47 static size_t direndpos; // end of dir before .onion within string
48 static size_t printstartpos; // where to start printing from
49 static size_t printlen; // precalculated, related to printstartpos
51 static pthread_mutex_t fout_mutex;
52 static FILE *fout;
53 static size_t numneedgenerate = 0;
54 static pthread_mutex_t keysgenerated_mutex;
55 static volatile size_t keysgenerated = 0;
56 static volatile int endwork = 0;
58 static void termhandler(int sig)
60 switch (sig) {
61 case SIGTERM:
62 case SIGINT:
63 endwork = 1;
64 break;
68 // filters stuff
70 struct binfilter {
71 u8 f[PUBLIC_LEN];
72 size_t len; // real len minus one
73 u8 mask;
74 } ;
76 #ifdef INTFILTER
77 #ifndef IFT
78 #define IFT u64
79 #endif
80 struct intfilter {
81 IFT f;
82 #ifndef BINSEARCH
83 IFT m;
84 #endif
85 } ;
86 VEC_STRUCT(ifiltervec,struct intfilter) ifilters;
87 #ifdef BINSEARCH
88 IFT ifiltermask;
89 #endif // BINSEARCH
90 #else // INTFILTER
91 VEC_STRUCT(bfiltervec,struct binfilter) bfilters;
92 #endif // INTFILTER
94 static void filters_init()
96 #ifdef INTFILTER
97 VEC_INIT(ifilters);
98 #else
99 VEC_INIT(bfilters);
100 #endif
103 #ifdef INTFILTER
104 // o - old filter, n - new
105 // return -1 - old stays, 0 - no conflict, 1 - new overrides old
106 // assumes masked bits are cleared already
107 #ifndef BINSEARCH
108 static inline int ifilter_conflict(struct intfilter *o,struct intfilter *n)
110 if ((o->f & n->m) != (n->f & o->m))
111 return 0;
112 // determine which filter contain less bits
113 if (o->m <= n->m)
114 return -1;
115 return 1;
117 #else // BINSEARCH
118 static inline int ifilter_conflict(struct intfilter *o,struct intfilter *n,IFT mask)
120 if ((o->f & mask) != (n->f & ifiltermask))
121 return 0;
122 if (ifiltermask <= mask)
123 return -1;
124 return 1;
126 #endif // BINSEARCH
127 #else // INTFILTER
128 // o - old filter, n - new
129 // return: -1 - old stays, 0 - no conflict, 1 - new overrides old
130 // assumes irrelevant bits are cleared already
131 static inline int bfilter_conflict(struct binfilter *o,struct binfilter *n)
133 for (size_t i = 0;i < sizeof(o->f);++i) {
134 u8 oo,nn;
135 if (i < n->len)
136 oo = o->f[i];
137 else if (i == n->len)
138 oo = o->f[i] & n->mask;
139 else
140 oo = 0;
141 if (i < o->len)
142 nn = n->f[i];
143 else if (i == o->len)
144 nn = n->f[i] & o->mask;
145 else
146 nn = 0;
147 if (oo != nn)
148 return 0;
150 // functional filters subset was the same
151 // determine which filter contain less bits
152 if (o->len < n->len)
153 return -1;
154 if (o->len > n->len)
155 return 1;
156 if (o->mask <= n->mask)
157 return -1;
158 return 1;
160 #endif
162 #ifdef INTFILTER
163 #ifdef BINSEARCH
165 * raw representation -- FF.FF.F0.00
166 * big endian -- 0xFFFFF000
167 * little endian -- 0x00F0FFFF
168 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
169 * 0x0000F000 + 1 -> 0x0000F001
170 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
171 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
172 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
173 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
174 * 0x00f00000 + 1 -> 0x00f00001
175 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
176 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
177 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
179 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
180 * 0x0FffFFff + 1 -> 0x10000000
181 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
182 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
183 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
184 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
185 * 0xFFffFF0f + 1 -> 0xFFffFF10
186 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
187 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
188 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
190 * above method doesn't work in some cases. better way:
191 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
192 * 0x800f0000 >> 16 -> 0x0000800f
193 * 0x0000800f + 1 -> 0x00008010
194 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
195 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
196 * essentially, we have to make direct mask + shifted mask bits worth of information
197 * and then split it into 2 parts
198 * we do not need absolute shifted mask shifting value, just relative to direct mask
199 * 0x0sss00dd - shifted & direct mask combo
200 * 0x000sssdd - combined mask
201 * 8 - relshiftval
202 * generate values from 0x00000000 to 0x000sssdd
203 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
204 * or..
205 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
207 static inline void ifilter_addflatten(struct intfilter *ifltr,IFT mask)
209 printf(">enter flatten,f:%08X,m:%08X\n",ifltr->f,mask);
210 if (VEC_LENGTH(ifilters) == 0) {
211 printf(">flatten simple\n");
212 // simple
213 VEC_ADD(ifilters,*ifltr);
214 ifiltermask = mask;
215 return;
217 if (ifiltermask == mask) {
218 printf(">flatten lucky\n");
219 // lucky, only need to insert at the right place
220 VEC_FOR(ifilters,i) {
221 if (VEC_BUF(ifilters,i).f > ifltr->f) {
222 VEC_INSERT(ifilters,i,*ifltr);
223 return;
226 VEC_ADD(ifilters,*ifltr);
227 return;
229 printf(">flatten complicated; em:0x%08X,m:0x%08X\n",ifiltermask,mask);
230 IFT cross = ifiltermask ^ mask;
231 printf(">cross:%08X\n",cross);
232 int ishift = 0;
233 while ((cross & 1) == 0) {
234 ++ishift;
235 cross >>= 1;
237 printf(">ishift:%d,cross:%08X\n",ishift,cross);
238 IFT smask = cross & (cross + 1); // shift mask
239 printf(">smask:%08X\n",smask);
240 IFT dmask = cross ^ smask; // direct mask
241 printf(">dmask:%08X\n",dmask);
242 IFT cmask; // combined mask
243 int rshift = 0; // relative shift
244 while (cmask = (smask >> rshift) | dmask,(cmask & (cmask + 1)) != 0)
245 ++rshift;
246 printf(">cmask:%08X,rshift:%d\n",cmask,rshift);
247 // preparations done
248 if (ifiltermask > mask) {
249 // already existing stuff has more precise mask than we
250 // so we need to flatten our stuff
251 // first find where we should insert
252 VEC_FOR(ifilters,i) {
253 if (VEC_BUF(ifilters,i).f > ifltr->f) {
254 // there
255 printf(">before insert:%d\n",(int)VEC_LENGTH(ifilters));
256 VEC_INSERTN(ifilters,i,cmask + 1);
257 printf(">after insert:%d\n",(int)VEC_LENGTH(ifilters));
258 printf(">afterval f:%08X\n",VEC_BUF(ifilters,i+cmask+1).f);
259 for (size_t j = 0;;++j) {
260 VEC_BUF(ifilters,i + j).f = ifltr->f | (((j & dmask) | ((j << rshift) & smask)) << ishift);
261 printf(">insert pos:%d,f:%08X\n",
262 (int)(i + j),VEC_BUF(ifilters,i + j).f);
263 if (j == cmask)
264 break;
266 return;
269 size_t i = VEC_LENGTH(ifilters);
270 VEC_ADDN(ifilters,cmask + 1);
271 for (size_t j = 0;;++j) {
272 VEC_BUF(ifilters,i + j).f = ifltr->f | (j & dmask) | ((j << rshift) & smask);
273 if (j == cmask)
274 break;
276 return;
278 assert(0);
280 #endif // BINSEARCH
281 #endif // INTFILTER
283 static void filters_add(const char *filter)
285 struct binfilter bf;
286 size_t ret, ret2;
287 #ifdef INTFILTER
288 union intconv {
289 IFT i;
290 u8 b[sizeof(IFT)];
291 } fc,mc;
292 #endif
294 memset(&bf,0,sizeof(bf));
296 if (!base32_valid(filter,&ret)) {
297 fprintf(stderr, "filter \"%s\" is invalid\n", filter);
298 return;
300 ret = BASE32_FROM_LEN(ret);
301 if (!ret)
302 return;
303 #ifdef INTFILTER
304 if (ret > sizeof(IFT))
305 #else
306 if (ret > PUBLIC_LEN)
307 #endif
309 fprintf(stderr, "filter \"%s\" is too long\n", filter);
310 return;
312 ret2 = base32_from(bf.f,&bf.mask,filter);
313 assert(ret == ret2);
314 bf.len = ret - 1;
315 #ifdef INTFILTER
316 mc.i = 0;
317 for (size_t i = 0;i < bf.len;++i)
318 mc.b[i] = 0xFF;
319 mc.b[bf.len] = bf.mask;
320 memcpy(fc.b,bf.f,sizeof(fc.b));
321 fc.i &= mc.i;
322 struct intfilter ifltr = {
323 .f = fc.i,
324 #ifndef BINSEARCH
325 .m = mc.i,
326 #endif
328 VEC_FOR(ifilters,i) {
329 int c;
330 #ifndef BINSEARCH
331 c = ifilter_conflict(&VEC_BUF(ifilters,i),&ifltr);
332 #else
333 c = ifilter_conflict(&VEC_BUF(ifilters,i),&ifltr,mc.i);
334 #endif
335 if (c < 0)
336 return; // old filter eats us
337 else if (c > 0) {
338 VEC_REMOVE(ifilters,i);
339 --i;
340 // we eat old filter
343 #ifdef BINSEARCH
344 ifilter_addflatten(&ifltr,mc.i);
345 #else
346 VEC_FOR(ifilters,i) {
347 // filter with least bits first
348 if (VEC_BUF(ifilters,i).m > ifltr.m) {
349 VEC_INSERT(ifilters,i,ifltr);
350 return;
353 VEC_ADD(ifilters,ifltr);
354 #endif // BINSEARCH
355 #else // INTFILTER
356 VEC_FOR(bfilters,i) {
357 int c = bfilter_conflict(&VEC_BUF(bfilters,i),&bf);
358 if (c < 0)
359 return; // old filter eats us
360 else if (c > 0) {
361 VEC_REMOVE(bfilters,i);
362 --i;
363 // we eat old filter
366 #ifdef BINSEARCH
367 // TODO
368 #else
369 VEC_FOR(bfilters,i) {
370 // filter with least bits first
371 if (VEC_BUF(bfilters,i).len > bf.len ||
372 (VEC_BUF(bfilters,i).len == bf.len &&
373 (VEC_BUF(bfilters,i).mask > bf.mask)))
375 VEC_INSERT(bfilters,i,bf);
376 return;
379 VEC_ADD(bfilters,bf);
380 #endif // BINSEARCH
381 #endif // INTFILTER
384 static void filters_clean()
386 #ifdef INTFILTER
387 VEC_FREE(ifilters);
388 #else
389 VEC_FREE(bfilters);
390 #endif
393 static size_t filters_count()
395 #ifdef INTFILTER
396 return VEC_LENGTH(ifilters);
397 #else
398 return VEC_LENGTH(bfilters);
399 #endif
402 #ifdef INTFILTER
404 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
405 #ifndef BINSEARCH
406 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
407 #else
408 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & ifiltermask) == VEC_BUF(ifilters,it).f)
409 #endif
411 #else
413 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
414 #define MATCHFILTER(it,pk) ( \
415 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
416 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
418 #endif
420 static void loadfilterfile(const char *fname)
422 char buf[128];
423 FILE *f = fopen(fname,"r");
424 while (fgets(buf,sizeof(buf),f)) {
425 for (char *p = buf;*p;++p) {
426 if (*p == '\n') {
427 *p = 0;
428 break;
431 if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0)
432 filters_add(buf);
436 static void printfilters()
438 size_t i,l;
439 #ifdef INTFILTER
440 l = VEC_LENGTH(ifilters);
441 #else
442 l = VEC_LENGTH(bfilters);
443 #endif
444 if (l)
445 fprintf(stderr, "filters:\n");
446 else
447 fprintf(stderr, "no filters defined\n");
449 for (i = 0;i < l;++i) {
450 char buf0[256],buf1[256];
451 u8 bufx[128];
452 #ifdef INTFILTER
453 size_t len = 0;
454 u8 *imraw;
455 #ifndef BINSEARCH
456 imraw = (u8 *)&VEC_BUF(ifilters,i).m;
457 #else
458 imraw = (u8 *)&ifiltermask;
459 #endif
460 while (len < sizeof(IFT) && imraw[len] != 0x00) ++len;
461 u8 mask = imraw[len-1];
462 u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f;
463 #else
464 size_t len = VEC_BUF(bfilters,i).len + 1;
465 u8 mask = VEC_BUF(bfilters,i).mask;
466 u8 *ifraw = VEC_BUF(bfilters,i).f;
467 #endif
468 base32_to(buf0,ifraw,len);
469 memcpy(bufx,ifraw,len);
470 bufx[len - 1] |= ~mask;
471 base32_to(buf1,bufx,len);
472 char *a = buf0,*b = buf1;
473 while (*a && *a == *b)
474 ++a, ++b;
475 *a = 0;
476 fprintf(stderr, "\t%s\n",buf0);
480 // statistics, if enabled
481 #ifdef STATISTICS
482 struct statstruct {
483 union {
484 u32 v;
485 size_t align;
486 } numcalc;
487 union {
488 u32 v;
489 size_t align;
490 } numsuccess;
492 VEC_STRUCT(statsvec,struct statstruct);
494 struct tstatstruct {
495 u64 numcalc;
496 u64 numsuccess;
497 u32 oldnumcalc;
498 u32 oldnumsuccess;
500 VEC_STRUCT(tstatsvec,struct tstatstruct);
501 #endif
504 static void onionready(char *sname, const u8 *secret, const u8 *pubonion)
506 FILE *fh;
508 if (endwork)
509 return;
511 if (numneedgenerate) {
512 pthread_mutex_lock(&keysgenerated_mutex);
513 if (keysgenerated >= numneedgenerate) {
514 pthread_mutex_unlock(&keysgenerated_mutex);
515 return;
519 if (mkdir(sname,0700) != 0) {
520 if (numneedgenerate)
521 pthread_mutex_unlock(&keysgenerated_mutex);
522 return;
525 if (numneedgenerate) {
526 ++keysgenerated;
527 if (keysgenerated >= numneedgenerate)
528 endwork = 1;
529 pthread_mutex_unlock(&keysgenerated_mutex);
532 strcpy(&sname[onionendpos], "/hs_ed25519_secret_key");
533 fh = fopen(sname, "wb");
534 if (fh) {
535 fwrite(secret, skprefixlen + SECRET_LEN, 1, fh);
536 fclose(fh);
539 strcpy(&sname[onionendpos], "/hostname");
540 fh = fopen(sname, "w");
541 if (fh) {
542 sname[onionendpos] = '\n';
543 fwrite(&sname[direndpos], ONIONLEN+1, 1, fh);
544 fclose(fh);
547 strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
548 fh = fopen(sname, "wb");
549 if (fh) {
550 fwrite(pubonion, pkprefixlen + PUBLIC_LEN, 1, fh);
551 fclose(fh);
554 if (fout) {
555 sname[onionendpos] = '\n';
556 pthread_mutex_lock(&fout_mutex);
557 fwrite(&sname[printstartpos], printlen, 1, fout);
558 fflush(fout);
559 pthread_mutex_unlock(&fout_mutex);
563 // little endian inc
564 static void addseed(u8 *seed)
566 register unsigned int c = 1;
567 for (size_t i = 0;i < SEED_LEN;++i) {
568 c = (unsigned int)seed[i] + c; seed[i] = c & 0xFF; c >>= 8;
569 // unsure if needed
570 if (!c) break;
574 static void *dowork(void *task)
576 union pubonionunion {
577 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
578 struct {
579 u64 prefix[4];
580 u64 key[4];
581 u64 hash[4];
582 } i;
583 } pubonion;
584 u8 * const pk = &pubonion.raw[pkprefixlen];
585 u8 secret[skprefixlen + SECRET_LEN];
586 u8 * const sk = &secret[skprefixlen];
587 u8 seed[SEED_LEN];
588 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
589 size_t i;
590 char *sname;
591 #ifdef STATISTICS
592 struct statstruct *st = (struct statstruct *)task;
593 #endif
595 memcpy(secret,skprefix,skprefixlen);
596 memcpy(pubonion.raw,pkprefix,pkprefixlen);
597 // write version later as it will be overwritten by hash
598 memcpy(hashsrc,checksumstr,checksumstrlen);
599 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
601 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
602 if (workdir)
603 memcpy(sname,workdir,workdirlen);
605 initseed:
606 randombytes(seed,sizeof(seed));
608 again:
609 if (unlikely(endwork))
610 goto end;
612 ed25519_seckey_expand(sk,seed);
613 ed25519_pubkey(pk,sk);
615 #ifdef STATISTICS
616 ++st->numcalc.v;
617 #endif
619 FILTERFOR(i) {
620 if (unlikely(MATCHFILTER(i,pk))) {
621 #ifdef STATISTICS
622 ++st->numsuccess.v;
623 #endif
624 // calc checksum
625 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
626 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
627 // version byte
628 pk[PUBLIC_LEN + 2] = 0x03;
629 // base32
630 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
631 onionready(sname, secret, pubonion.raw);
632 goto initseed;
635 addseed(seed);
636 goto again;
638 end:
639 free(sname);
640 return 0;
643 static void addu64toscalar32(u8 *dst,u64 v)
645 int i;
646 u32 c = 0;
647 for (i = 0;i < 32;++i) {
648 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
649 v >>= 8;
650 ++dst;
654 static void *dofastwork(void *task)
656 union pubonionunion {
657 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
658 struct {
659 u64 prefix[4];
660 u64 key[4];
661 u64 hash[4];
662 } i;
663 } pubonion;
664 u8 * const pk = &pubonion.raw[pkprefixlen];
665 u8 secret[skprefixlen + SECRET_LEN];
666 u8 * const sk = &secret[skprefixlen];
667 u8 seed[SEED_LEN];
668 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
669 ge_p3 ge_public;
670 u64 counter;
671 size_t i;
672 char *sname;
673 #ifdef STATISTICS
674 struct statstruct *st = (struct statstruct *)task;
675 #endif
677 memcpy(secret, skprefix, skprefixlen);
678 memcpy(pubonion.raw, pkprefix, pkprefixlen);
679 // write version later as it will be overwritten by hash
680 memcpy(hashsrc, checksumstr, checksumstrlen);
681 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
683 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
684 if (workdir)
685 memcpy(sname, workdir, workdirlen);
687 initseed:
688 randombytes(seed,sizeof(seed));
689 ed25519_seckey_expand(sk,seed);
691 ge_scalarmult_base(&ge_public,sk);
692 ge_p3_tobytes(pk,&ge_public);
694 for (counter = 0;counter < U64_MAX-8;counter += 8) {
695 ge_p1p1 sum;
697 if (unlikely(endwork))
698 goto end;
700 FILTERFOR(i) {
701 if (unlikely(MATCHFILTER(i,pk))) {
702 // found!
703 // update secret key with counter
704 addu64toscalar32(sk,counter);
705 // sanity check
706 if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
707 /* These operations should be a no-op. */
708 sk[0] &= 248;
709 sk[31] &= 63;
710 sk[31] |= 64;
712 else goto initseed;
713 #ifdef STATISTICS
714 ++st->numsuccess.v;
715 #endif
716 // calc checksum
717 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
718 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
719 // version byte
720 pk[PUBLIC_LEN + 2] = 0x03;
721 // full name
722 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
723 onionready(sname,secret,pubonion.raw);
724 // don't reuse same seed
725 goto initseed;
729 // next
730 ge_add(&sum, &ge_public,&ge_eightpoint);
731 ge_p1p1_to_p3(&ge_public,&sum);
732 ge_p3_tobytes(pk,&ge_public);
733 #ifdef STATISTICS
734 ++st->numcalc.v;
735 #endif
737 goto initseed;
739 end:
740 free(sname);
741 return 0;
744 void printhelp(const char *progname)
746 fprintf(stderr,
747 "Usage: %s filter [filter...] [options]\n"
748 " %s -f filterfile [options]\n"
749 "Options:\n"
750 "\t-h - print help\n"
751 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
752 "\t-q - do not print diagnostic output to stderr\n"
753 "\t-x - do not print onion names\n"
754 "\t-o filename - output onion names to specified file\n"
755 "\t-F - include directory names in onion names output\n"
756 "\t-d dirname - output directory\n"
757 "\t-t numthreads - specify number of threads (default - auto)\n"
758 "\t-j numthreads - same as -t\n"
759 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
760 "\t-z - use faster key generation method. this is now default\n"
761 "\t-Z - use slower key generation method\n"
762 "\t-s - print statistics each 10 seconds\n"
763 "\t-S t - print statistics every specified ammount of seconds\n"
764 "\t-T - do not reset statistics counters when printing\n"
765 ,progname,progname);
766 exit(1);
769 void setworkdir(const char *wd)
771 free(workdir);
772 size_t l = strlen(wd);
773 if (!l) {
774 workdir = 0;
775 workdirlen = 0;
776 if (!quietflag)
777 fprintf(stderr, "unset workdir\n");
778 return;
780 int needslash = 0;
781 if (wd[l-1] != '/')
782 needslash = 1;
783 char *s = malloc(l + needslash + 1);
784 memcpy(s, wd, l);
785 if (needslash)
786 s[l++] = '/';
787 s[l] = 0;
789 workdir = s;
790 workdirlen = l;
791 if (!quietflag)
792 fprintf(stderr,"set workdir: %s\n",workdir);
795 VEC_STRUCT(threadvec, pthread_t);
797 int main(int argc,char **argv)
799 char *outfile = 0;
800 const char *arg;
801 int ignoreargs = 0;
802 int dirnameflag = 0;
803 int numthreads = 0;
804 int fastkeygen = 1;
805 struct threadvec threads;
806 #ifdef STATISTICS
807 struct statsvec stats;
808 struct tstatsvec tstats;
809 u64 reportdelay = 0;
810 int realtimestats = 1;
811 #endif
812 int tret;
814 ge_initeightpoint();
815 filters_init();
817 fout = stdout;
818 pthread_mutex_init(&keysgenerated_mutex, 0);
819 pthread_mutex_init(&fout_mutex, 0);
821 const char *progname = argv[0];
822 if (argc <= 1)
823 printhelp(progname);
824 argc--, argv++;
826 while (argc--) {
827 arg = *argv++;
828 if (!ignoreargs && *arg == '-') {
829 int numargit = 0;
830 nextarg:
831 ++arg;
832 ++numargit;
833 if (*arg == '-') {
834 if (numargit > 1) {
835 fprintf(stderr, "unrecognised argument: -\n");
836 exit(1);
838 ++arg;
839 if (!*arg)
840 ignoreargs = 1;
841 else if (!strcmp(arg, "help"))
842 printhelp(progname);
843 else {
844 fprintf(stderr, "unrecognised argument: --%s\n", arg);
845 exit(1);
847 numargit = 0;
849 else if (*arg == 0) {
850 if (numargit == 1)
851 ignoreargs = 1;
852 continue;
854 else if (*arg == 'h')
855 printhelp(progname);
856 else if (*arg == 'f') {
857 if (argc--)
858 loadfilterfile(*argv++);
859 else {
860 fprintf(stderr, "additional argument required\n");
861 exit(1);
864 else if (*arg == 'q')
865 ++quietflag;
866 else if (*arg == 'x')
867 fout = 0;
868 else if (*arg == 'o') {
869 if (argc--)
870 outfile = *argv++;
871 else {
872 fprintf(stderr, "additional argument required\n");
873 exit(1);
876 else if (*arg == 'F')
877 dirnameflag = 1;
878 else if (*arg == 'd') {
879 if (argc--) {
880 setworkdir(*argv++);
882 else {
883 fprintf(stderr, "additional argument required\n");
886 else if (*arg == 't' || *arg == 'j') {
887 if (argc--)
888 numthreads = atoi(*argv++);
889 else {
890 fprintf(stderr, "additional argument required\n");
891 exit(1);
894 else if (*arg == 'n') {
895 if (argc--)
896 numneedgenerate = (size_t)atoll(*argv++);
897 else {
898 fprintf(stderr, "additional argument required\n");
899 exit(1);
902 else if (*arg == 'Z')
903 fastkeygen = 0;
904 else if (*arg == 'z')
905 fastkeygen = 1;
906 else if (*arg == 's') {
907 #ifdef STATISTICS
908 reportdelay = 10000000;
909 #else
910 fprintf(stderr,"statistics support not compiled in\n");
911 exit(1);
912 #endif
914 else if (*arg == 'S') {
915 #ifdef STATISTICS
916 if (argc--)
917 reportdelay = (u64)atoll(*argv++) * 1000000;
918 else {
919 fprintf(stderr, "additional argument required\n");
920 exit(1);
922 #else
923 fprintf(stderr,"statistics support not compiled in\n");
924 exit(1);
925 #endif
927 else if (*arg == 'T') {
928 #ifdef STATISTICS
929 realtimestats = 0;
930 #else
931 fprintf(stderr,"statistics support not compiled in\n");
932 exit(1);
933 #endif
935 else {
936 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
937 exit(1);
939 if (numargit)
940 goto nextarg;
942 else filters_add(arg);
945 if (!quietflag)
946 printfilters();
948 if (!filters_count())
949 return 0;
951 if (outfile)
952 fout = fopen(outfile, "w");
954 if (workdir)
955 mkdir(workdir, 0700);
957 direndpos = workdirlen;
958 onionendpos = workdirlen + ONIONLEN;
960 if (!dirnameflag) {
961 printstartpos = direndpos;
962 printlen = ONIONLEN + 1;
963 } else {
964 printstartpos = 0;
965 printlen = onionendpos + 1;
968 if (numthreads <= 0) {
969 numthreads = cpucount();
970 if (numthreads <= 0)
971 numthreads = 1;
974 signal(SIGTERM,termhandler);
975 signal(SIGINT,termhandler);
977 VEC_INIT(threads);
978 VEC_ADDN(threads,numthreads);
979 #ifdef STATISTICS
980 VEC_INIT(stats);
981 VEC_ADDN(stats,numthreads);
982 VEC_ZERO(stats);
983 VEC_INIT(tstats);
984 VEC_ADDN(tstats,numthreads);
985 VEC_ZERO(tstats);
986 #endif
988 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
989 void *tp = 0;
990 #ifdef STATISTICS
991 tp = &VEC_BUF(stats,i);
992 #endif
993 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
994 if (tret) {
995 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
996 exit(1);
1000 #ifdef STATISTICS
1001 struct timespec nowtime;
1002 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
1003 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
1004 fprintf(stderr, "failed to get time\n");
1005 exit(1);
1007 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1008 #endif
1009 struct timespec ts;
1010 memset(&ts,0,sizeof(ts));
1011 ts.tv_nsec = 100000000;
1012 while (!endwork) {
1013 if (numneedgenerate && keysgenerated >= numneedgenerate) {
1014 endwork = 1;
1015 break;
1017 nanosleep(&ts,0);
1019 #ifdef STATISTICS
1020 clock_gettime(CLOCK_MONOTONIC,&nowtime);
1021 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1022 u64 sumcalc = 0,sumsuccess = 0;
1023 for (size_t i = 0;i < numthreads;++i) {
1024 u32 newt,tdiff;
1025 // numcalc
1026 newt = VEC_BUF(stats,i).numcalc.v;
1027 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
1028 VEC_BUF(tstats,i).oldnumcalc = newt;
1029 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
1030 sumcalc += VEC_BUF(tstats,i).numcalc;
1031 // numsuccess
1032 newt = VEC_BUF(stats,i).numsuccess.v;
1033 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
1034 VEC_BUF(tstats,i).oldnumsuccess = newt;
1035 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
1036 sumsuccess += VEC_BUF(tstats,i).numsuccess;
1038 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
1039 if (ireporttime)
1040 ireporttime += reportdelay;
1041 else
1042 ireporttime = inowtime;
1043 if (!ireporttime)
1044 ireporttime = 1;
1046 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
1047 double successpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
1048 fprintf(stderr,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec,successpersec,(inowtime - istarttime + elapsedoffset) / 1000000.0);
1050 if (realtimestats) {
1051 for (size_t i = 0;i < numthreads;++i) {
1052 VEC_BUF(tstats,i).numcalc = 0;
1053 VEC_BUF(tstats,i).numsuccess = 0;
1055 elapsedoffset += inowtime - istarttime;
1056 istarttime = inowtime;
1059 if (sumcalc > U64_MAX / 2) {
1060 for (size_t i = 0;i < numthreads;++i) {
1061 VEC_BUF(tstats,i).numcalc /= 2;
1062 VEC_BUF(tstats,i).numsuccess /= 2;
1064 u64 timediff = (inowtime - istarttime + 1) / 2;
1065 elapsedoffset += timediff;
1066 istarttime += timediff;
1068 #endif
1071 if (!quietflag)
1072 fprintf(stderr, "waiting for threads to finish...\n");
1073 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
1074 pthread_join(VEC_BUF(threads,i),0);
1075 if (!quietflag)
1076 fprintf(stderr, "done, quitting\n");
1078 pthread_mutex_destroy(&keysgenerated_mutex);
1079 pthread_mutex_destroy(&fout_mutex);
1080 filters_clean();
1082 if (outfile)
1083 fclose(fout);
1085 return 0;