-Z should be a bit faster now
[mkp224o.git] / main.c
blob63bf3a479fcc4d64a5190c7965025922737c340f
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 <time.h>
10 #include <pthread.h>
11 #include <signal.h>
12 #include <sodium/randombytes.h>
14 #include "types.h"
15 #include "likely.h"
16 #include "vec.h"
17 #include "base32.h"
18 #include "cpucount.h"
19 #include "keccak.h"
20 #include "ed25519/ed25519.h"
21 #include "ioutil.h"
23 // additional 0 terminator is added by C
24 static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
25 #define pkprefixlen (29 + 3)
26 static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
27 #define skprefixlen (29 + 3)
28 static const char * const checksumstr = ".onion checksum";
29 #define checksumstrlen 15
31 // output directory
32 static char *workdir = 0;
33 static size_t workdirlen = 0;
35 static int quietflag = 0;
37 #define SECRET_LEN 64
38 #define PUBLIC_LEN 32
39 #define SEED_LEN 32
40 // with checksum + version num
41 #define PUBONION_LEN (PUBLIC_LEN + 3)
42 // with newline included
43 #define ONIONLEN 62
45 static size_t onionendpos; // end of .onion within string
46 static size_t direndpos; // end of dir before .onion within string
47 static size_t printstartpos; // where to start printing from
48 static size_t printlen; // precalculated, related to printstartpos
50 static pthread_mutex_t fout_mutex;
51 static FILE *fout;
52 static size_t numneedgenerate = 0;
53 static int numwords = 1;
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 #ifdef INTFILTER
71 #ifdef BINSEARCH
72 #ifndef BESORT
73 #define OMITMASK
74 #endif
75 #endif
76 #endif
78 #ifdef OMITMASK
79 #define EXPANDMASK
80 #endif
82 #ifndef BINFILTERLEN
83 #define BINFILTERLEN PUBLIC_LEN
84 #endif
85 struct binfilter {
86 u8 f[BINFILTERLEN];
87 size_t len; // real len minus one
88 u8 mask;
89 } ;
91 #ifdef INTFILTER
92 struct intfilter {
93 IFT f;
94 # ifndef OMITMASK
95 IFT m;
96 # endif
97 } ;
98 VEC_STRUCT(ifiltervec,struct intfilter) ifilters;
99 # ifdef OMITMASK
100 IFT ifiltermask;
101 # endif // BINSEARCH
102 #else // INTFILTER
103 VEC_STRUCT(bfiltervec,struct binfilter) bfilters;
104 #endif // INTFILTER
106 static void filters_init()
108 #ifdef INTFILTER
109 VEC_INIT(ifilters);
110 #else
111 VEC_INIT(bfilters);
112 #endif
115 #ifdef INTFILTER
117 static void filter_sort(int (*compf)(const void *,const void *))
119 qsort(&VEC_BUF(ifilters,0),VEC_LENGTH(ifilters),sizeof(struct intfilter),compf);
122 static inline size_t filter_len(size_t i)
124 # ifndef OMITMASK
125 const u8 *m = (const u8 *)&VEC_BUF(ifilters,i).m;
126 # else // OMITMASK
127 const u8 *m = (const u8 *)&ifiltermask;
128 # endif // OMITMASK
129 size_t c = 0;
130 for (size_t j = 0;;) {
131 u8 v = m[j];
132 for (size_t k = 0;;) {
133 if (!v)
134 return c;
135 ++c;
136 if (++k >= 8)
137 break;
138 v <<= 1;
140 if (++j >= sizeof(IFT))
141 break;
143 return c;
146 # ifdef OMITMASK
148 static int filter_compare(const void *p1,const void *p2)
150 if (((const struct intfilter *)p1)->f < ((const struct intfilter *)p2)->f)
151 return -1;
152 if (((const struct intfilter *)p1)->f > ((const struct intfilter *)p2)->f)
153 return 1;
154 return 0;
157 # ifdef EXPANDMASK
160 * raw representation -- FF.FF.F0.00
161 * big endian -- 0xFFFFF000
162 * little endian -- 0x00F0FFFF
163 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
164 * 0x0000F000 + 1 -> 0x0000F001
165 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
166 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
167 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
168 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
169 * 0x00f00000 + 1 -> 0x00f00001
170 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
171 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
172 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
174 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
175 * 0x0FffFFff + 1 -> 0x10000000
176 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
177 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
178 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
179 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
180 * 0xFFffFF0f + 1 -> 0xFFffFF10
181 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
182 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
183 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
185 * essentially, we have to make direct mask + shifted mask bits worth of information
186 * and then split it into 2 parts
187 * we do not need absolute shifted mask shifting value, just relative to direct mask
188 * 0x0sss00dd - shifted & direct mask combo
189 * 0x000sssdd - combined mask
190 * 8 - relshiftval
191 * generate values from 0x00000000 to 0x000sssdd
192 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
193 * or..
194 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
195 * ...
196 * above method doesn't work in some cases. better way:
197 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
198 * 0x800f0000 >> 16 -> 0x0000800f
199 * 0x0000800f + 1 -> 0x00008010
200 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
201 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
204 #define EXPVAL(init,j,dmask,smask,ishift,rshift) \
205 ((init) | ((((j) & (dmask)) | (((j) << (rshift)) & (smask))) << (ishift)))
206 // add expanded set of values
207 // allocates space on its own
208 static void ifilter_addexpanded(
209 struct intfilter *ifltr,
210 IFT dmask,IFT smask,IFT cmask,
211 int ishift,int rshift)
213 size_t i = VEC_LENGTH(ifilters);
214 VEC_ADDN(ifilters,cmask + 1);
215 for (size_t j = 0;;++j) {
216 VEC_BUF(ifilters,i + j).f = EXPVAL(ifltr->f,j,dmask,smask,ishift,rshift);
217 if (j == cmask)
218 break;
222 // expand existing stuff
223 // allocates needed stuff on its own
224 static void ifilter_expand(IFT dmask,IFT smask,IFT cmask,int ishift,int rshift)
226 size_t len = VEC_LENGTH(ifilters);
227 VEC_ADDN(ifilters,cmask * len);
228 size_t esz = cmask + 1; // size of expanded elements
229 for (size_t i = len - 1;;--i) {
230 for (IFT j = 0;;++j) {
231 VEC_BUF(ifilters,i * esz + j).f = EXPVAL(VEC_BUF(ifilters,i).f,j,dmask,smask,ishift,rshift);
232 if (j == cmask)
233 break;
235 if (i == 0)
236 break;
240 static inline void ifilter_addflatten(struct intfilter *ifltr,IFT mask)
242 if (VEC_LENGTH(ifilters) == 0) {
243 // simple
244 VEC_ADD(ifilters,*ifltr);
245 ifiltermask = mask;
246 return;
248 if (ifiltermask == mask) {
249 // lucky
250 VEC_ADD(ifilters,*ifltr);
251 return;
253 IFT cross = ifiltermask ^ mask;
254 int ishift = 0;
255 while ((cross & 1) == 0) {
256 ++ishift;
257 cross >>= 1;
259 IFT smask = cross & (cross + 1); // shift mask
260 IFT dmask = cross ^ smask; // direct mask
261 IFT cmask; // combined mask
262 int rshift = 0; // relative shift
263 while (cmask = (smask >> rshift) | dmask,(cmask & (cmask + 1)) != 0)
264 ++rshift;
265 // preparations done
266 if (ifiltermask > mask) {
267 // already existing stuff has more precise mask than we
268 // so we need to expand our stuff
269 ifilter_addexpanded(ifltr,dmask,smask,cmask,ishift,rshift);
271 else {
272 ifiltermask = mask;
273 ifilter_expand(dmask,smask,cmask,ishift,rshift);
274 VEC_ADD(ifilters,*ifltr);
278 # endif // EXPANDMASK
280 # else // OMITMASK
283 * struct intfilter layout: filter,mask
284 * stuff is compared in big-endian way, so memcmp
285 * filter needs to be compared first
286 * if its equal, mask needs to be compared
287 * memcmp is aplicable there too
288 * due to struct intfilter layout, it all can be stuffed into one memcmp call
290 static int filter_compare(const void *p1,const void *p2)
292 return memcmp(p1,p2,sizeof(struct intfilter));
295 # endif // OMITMASK
296 #else // INTFILTER
298 static inline size_t filter_len(size_t i)
300 size_t c = VEC_BUF(bfilters,i).len * 8;
301 u8 v = VEC_BUF(bfilters,i).mask;
302 for (size_t k = 0;;) {
303 if (!v)
304 return c;
305 ++c;
306 if (++k >= 8)
307 return c;
308 v <<= 1;
312 static void filter_sort(int (*compf)(const void *,const void *))
314 qsort(&VEC_BUF(bfilters,0),VEC_LENGTH(bfilters),sizeof(struct binfilter),compf);
317 static int filter_compare(const void *p1,const void *p2)
319 const struct binfilter *b1 = (const struct binfilter *)p1;
320 const struct binfilter *b2 = (const struct binfilter *)p2;
321 size_t l = b1->len <= b2->len ? b1->len : b2->len;
322 int cmp = memcmp(b1->f,b2->f,l);
323 if (cmp)
324 return cmp;
325 if (b1->len < b2->len)
326 return -1;
327 if (b1->len > b2->len)
328 return 1;
329 if (b1->mask < b2->mask)
330 return -1;
331 if (b1->mask > b2->mask)
332 return 1;
333 return 0;
336 #endif // INTFILTER
338 static void filters_add(const char *filter)
340 struct binfilter bf;
341 size_t ret;
342 #ifdef INTFILTER
343 union intconv {
344 IFT i;
345 u8 b[sizeof(IFT)];
346 } fc,mc;
347 #endif
349 // skip regex start symbol. we do not support regex tho
350 if (*filter == '^')
351 ++filter;
353 memset(&bf,0,sizeof(bf));
355 if (!base32_valid(filter,&ret)) {
356 fprintf(stderr,"filter \"%s\" is invalid\n",filter);
357 return;
359 ret = BASE32_FROM_LEN(ret);
360 if (!ret)
361 return;
362 #ifdef INTFILTER
363 if (ret > sizeof(IFT))
364 #else
365 if (ret > sizeof(bf.f))
366 #endif
368 fprintf(stderr,"filter \"%s\" is too long\n",filter);
369 return;
371 base32_from(bf.f,&bf.mask,filter);
372 bf.len = ret - 1;
373 #ifdef INTFILTER
374 mc.i = 0;
375 for (size_t i = 0;i < bf.len;++i)
376 mc.b[i] = 0xFF;
377 mc.b[bf.len] = bf.mask;
378 memcpy(fc.b,bf.f,sizeof(fc.b));
379 fc.i &= mc.i;
380 struct intfilter ifltr = {
381 .f = fc.i,
382 # ifndef OMITMASK
383 .m = mc.i,
384 # endif
386 # ifdef OMITMASK
387 ifilter_addflatten(&ifltr,mc.i);
388 # else // OMITMASK
389 VEC_ADD(ifilters,ifltr);
390 # endif // OMITMASK
391 #else // INTFILTER
392 VEC_ADD(bfilters,bf);
393 #endif // INTFILTER
396 static void filters_prepare()
398 if (!quietflag)
399 fprintf(stderr,"sorting filters...");
400 filter_sort(&filter_compare);
401 if (!quietflag)
402 fprintf(stderr," done.\n");
403 // TODO remove duplicates
406 static void filters_clean()
408 #ifdef INTFILTER
409 VEC_FREE(ifilters);
410 #else
411 VEC_FREE(bfilters);
412 #endif
415 static size_t filters_count()
417 #ifdef INTFILTER
418 return VEC_LENGTH(ifilters);
419 #else
420 return VEC_LENGTH(bfilters);
421 #endif
424 #ifdef STATISTICS
425 #define ADDNUMSUCCESS ++st->numsuccess.v
426 #else
427 #define ADDNUMSUCCESS do ; while (0)
428 #endif
430 #ifdef INTFILTER
432 # ifndef BINSEARCH
434 #define MATCHFILTER(it,pk) \
435 ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
437 #define DOFILTER(it,pk,code) \
438 do { \
439 for (it = 0;it < VEC_LENGTH(ifilters);++it) { \
440 if (unlikely(MATCHFILTER(it,pk))) { \
441 code; \
442 break; \
445 } while (0)
447 # else // BINSEARCH
449 # ifdef OMITMASK
451 #define DOFILTER(it,pk,code) \
452 do { \
453 register IFT maskedpk = *(IFT *)(pk) & ifiltermask; \
454 for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \
455 it = (up + down) / 2; \
456 if (maskedpk < VEC_BUF(ifilters,it).f) \
457 up = it; \
458 else if (maskedpk > VEC_BUF(ifilters,it).f) \
459 down = it + 1; \
460 else { \
461 code; \
462 break; \
465 } while (0)
467 # else // OMITMASK
469 #define DOFILTER(it,pk,code) \
470 do { \
471 for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \
472 it = (up + down) / 2; \
473 IFT maskedpk = *(IFT *)(pk) & VEC_BUF(ifilters,it).m; \
474 register int cmp = memcmp(&maskedpk,&VEC_BUF(ifilters,it).f,sizeof(IFT)); \
475 if (cmp < 0) \
476 up = it; \
477 else if (cmp > 0) \
478 down = it + 1; \
479 else { \
480 code; \
481 break; \
484 } while (0)
486 # endif // OMITMASK
488 # endif // BINSEARCH
490 #else // INTFILTER
492 # ifndef BINSEARCH
494 #define MATCHFILTER(it,pk) ( \
495 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
496 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
498 #define DOFILTER(it,pk,code) \
499 do { \
500 for (it = 0;it < VEC_LENGTH(bfilters);++it) { \
501 if (unlikely(MATCHFILTER(it,pk))) { \
502 code; \
503 break; \
506 } while (0)
508 # else // BINSEARCH
510 #define DOFILTER(it,pk,code) \
511 do { \
512 for (size_t down = 0,up = VEC_LENGTH(bfilters);down < up;) { \
513 it = (up + down) / 2; \
515 register int filterdiff = memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len); \
516 if (filterdiff < 0) { \
517 up = it; \
518 continue; \
520 if (filterdiff > 0) { \
521 down = it + 1; \
522 continue; \
525 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) < \
526 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
528 up = it; \
529 continue; \
531 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) > \
532 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
534 down = it + 1; \
535 continue; \
538 code; \
539 break; \
542 } while (0)
544 # endif // BINSEARCH
546 #endif // INTFILTER
548 static void loadfilterfile(const char *fname)
550 char buf[128];
551 FILE *f = fopen(fname,"r");
552 while (fgets(buf,sizeof(buf),f)) {
553 for (char *p = buf;*p;++p) {
554 if (*p == '\n') {
555 *p = 0;
556 break;
559 if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0)
560 filters_add(buf);
564 static void filters_print()
566 if (quietflag)
567 return;
568 size_t i,l;
569 #ifdef INTFILTER
570 l = VEC_LENGTH(ifilters);
571 #else
572 l = VEC_LENGTH(bfilters);
573 #endif
574 if (l)
575 fprintf(stderr, "filters:\n");
576 else
577 fprintf(stderr, "no filters defined\n");
579 for (i = 0;i < l;++i) {
580 char buf0[256],buf1[256];
581 u8 bufx[128];
583 if (i >= 20) {
584 fprintf(stderr,"[another %llu filters not shown]\n",(unsigned long long)(filters_count() - 20));
585 break;
588 #ifdef INTFILTER
589 size_t len = 0;
590 u8 *imraw;
592 # ifndef OMITMASK
593 imraw = (u8 *)&VEC_BUF(ifilters,i).m;
594 # else
595 imraw = (u8 *)&ifiltermask;
596 # endif
597 while (len < sizeof(IFT) && imraw[len] != 0x00) ++len;
598 u8 mask = imraw[len-1];
599 u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f;
600 #else
601 size_t len = VEC_BUF(bfilters,i).len + 1;
602 u8 mask = VEC_BUF(bfilters,i).mask;
603 u8 *ifraw = VEC_BUF(bfilters,i).f;
604 #endif
605 base32_to(buf0,ifraw,len);
606 memcpy(bufx,ifraw,len);
607 bufx[len - 1] |= ~mask;
608 base32_to(buf1,bufx,len);
609 char *a = buf0,*b = buf1;
610 while (*a && *a == *b)
611 ++a, ++b;
612 *a = 0;
613 fprintf(stderr, "\t%s\n",buf0);
617 // statistics, if enabled
618 #ifdef STATISTICS
619 struct statstruct {
620 union {
621 u32 v;
622 size_t align;
623 } numcalc;
624 union {
625 u32 v;
626 size_t align;
627 } numsuccess;
628 union {
629 u32 v;
630 size_t align;
631 } numrestart;
633 VEC_STRUCT(statsvec,struct statstruct);
635 struct tstatstruct {
636 u64 numcalc;
637 u64 numsuccess;
638 u64 numrestart;
639 u32 oldnumcalc;
640 u32 oldnumsuccess;
641 u32 oldnumrestart;
643 VEC_STRUCT(tstatsvec,struct tstatstruct);
644 #endif
647 static void onionready(char *sname, const u8 *secret, const u8 *pubonion)
649 if (endwork)
650 return;
652 if (numneedgenerate) {
653 pthread_mutex_lock(&keysgenerated_mutex);
654 if (keysgenerated >= numneedgenerate) {
655 pthread_mutex_unlock(&keysgenerated_mutex);
656 return;
660 if (createdir(sname,1) != 0) {
661 if (numneedgenerate)
662 pthread_mutex_unlock(&keysgenerated_mutex);
663 return;
666 if (numneedgenerate) {
667 ++keysgenerated;
668 if (keysgenerated >= numneedgenerate)
669 endwork = 1;
670 pthread_mutex_unlock(&keysgenerated_mutex);
673 strcpy(&sname[onionendpos], "/hs_ed25519_secret_key");
674 writetofile(sname,secret,skprefixlen + SECRET_LEN,1);
676 strcpy(&sname[onionendpos], "/hostname");
677 FILE *hfile = fopen(sname,"w");
678 if (hfile) {
679 sname[onionendpos] = '\n';
680 fwrite(&sname[direndpos],ONIONLEN + 1,1,hfile);
681 fclose(hfile);
684 strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
685 writetofile(sname,pubonion,pkprefixlen + PUBLIC_LEN,0);
687 if (fout) {
688 sname[onionendpos] = '\n';
689 pthread_mutex_lock(&fout_mutex);
690 fwrite(&sname[printstartpos], printlen, 1, fout);
691 fflush(fout);
692 pthread_mutex_unlock(&fout_mutex);
696 // little endian inc
697 static void addsk32(u8 *sk)
699 register unsigned int c = 8;
700 for (size_t i = 0;i < 32;++i) {
701 c = (unsigned int)sk[i] + c; sk[i] = c & 0xFF; c >>= 8;
702 // unsure if needed
703 if (!c) break;
707 // 0123 4567 xxxx --3--> 3456 7xxx
708 // 0123 4567 xxxx --1--> 1234 567x
709 static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits)
711 size_t i,sbytes = sbits / 8;
712 sbits %= 8;
713 for (i = 0;i + sbytes < PUBLIC_LEN;++i) {
714 dst[i] = (src[i+sbytes] << sbits) |
715 (src[i+sbytes+1] >> (8 - sbits));
717 for(;i < PUBLIC_LEN;++i)
718 dst[i] = 0;
721 static void *dowork(void *task)
723 union pubonionunion {
724 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
725 struct {
726 u64 prefix[4];
727 u64 key[4];
728 u64 hash[4];
729 } i;
730 } pubonion;
731 u8 * const pk = &pubonion.raw[pkprefixlen];
732 u8 secret[skprefixlen + SECRET_LEN];
733 u8 * const sk = &secret[skprefixlen];
734 u8 seed[SEED_LEN];
735 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
736 u8 wpk[PUBLIC_LEN + 1];
737 size_t i;
738 char *sname;
739 #ifdef STATISTICS
740 struct statstruct *st = (struct statstruct *)task;
741 #endif
743 memcpy(secret,skprefix,skprefixlen);
744 wpk[PUBLIC_LEN] = 0;
745 memset(&pubonion,0,sizeof(pubonion));
746 memcpy(pubonion.raw,pkprefix,pkprefixlen);
747 // write version later as it will be overwritten by hash
748 memcpy(hashsrc,checksumstr,checksumstrlen);
749 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
751 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
752 if (!sname)
753 abort();
754 if (workdir)
755 memcpy(sname,workdir,workdirlen);
757 initseed:
758 randombytes(seed,sizeof(seed));
759 ed25519_seckey_expand(sk,seed);
760 #ifdef STATISTICS
761 ++st->numrestart.v;
762 #endif
764 again:
765 if (unlikely(endwork))
766 goto end;
768 ed25519_pubkey(pk,sk);
770 #ifdef STATISTICS
771 ++st->numcalc.v;
772 #endif
774 DOFILTER(i,pk,{
775 if (numwords > 1) {
776 shiftpk(wpk,pk,filter_len(i));
777 size_t j;
778 for (int w = 1;;) {
779 DOFILTER(j,wpk,goto secondfind);
780 goto next;
781 secondfind:
782 if (++w >= numwords)
783 break;
784 shiftpk(wpk,wpk,filter_len(j));
787 // sanity check
788 if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31])
789 goto initseed;
791 ADDNUMSUCCESS;
793 // calc checksum
794 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
795 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
796 // version byte
797 pk[PUBLIC_LEN + 2] = 0x03;
798 // base32
799 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
800 onionready(sname,secret,pubonion.raw);
801 pk[PUBLIC_LEN] = 0;
802 goto initseed;
804 next:
805 addsk32(sk);
806 goto again;
808 end:
809 free(sname);
810 return 0;
813 static void addsztoscalar32(u8 *dst,size_t v)
815 int i;
816 u32 c = 0;
817 for (i = 0;i < 32;++i) {
818 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
819 v >>= 8;
820 ++dst;
824 static void *dofastwork(void *task)
826 union pubonionunion {
827 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
828 struct {
829 u64 prefix[4];
830 u64 key[4];
831 u64 hash[4];
832 } i;
833 } pubonion;
834 u8 * const pk = &pubonion.raw[pkprefixlen];
835 u8 secret[skprefixlen + SECRET_LEN];
836 u8 * const sk = &secret[skprefixlen];
837 u8 seed[SEED_LEN];
838 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
839 u8 wpk[PUBLIC_LEN + 1];
840 ge_p3 ge_public;
841 size_t counter;
842 size_t i;
843 char *sname;
844 #ifdef STATISTICS
845 struct statstruct *st = (struct statstruct *)task;
846 #endif
848 memcpy(secret, skprefix, skprefixlen);
849 wpk[PUBLIC_LEN] = 0;
850 memset(&pubonion,0,sizeof(pubonion));
851 memcpy(pubonion.raw,pkprefix,pkprefixlen);
852 // write version later as it will be overwritten by hash
853 memcpy(hashsrc,checksumstr,checksumstrlen);
854 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
856 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
857 if (!sname)
858 abort();
859 if (workdir)
860 memcpy(sname,workdir,workdirlen);
862 initseed:
863 #ifdef STATISTICS
864 ++st->numrestart.v;
865 #endif
866 randombytes(seed,sizeof(seed));
867 ed25519_seckey_expand(sk,seed);
869 ge_scalarmult_base(&ge_public,sk);
870 ge_p3_tobytes(pk,&ge_public);
872 for (counter = 0;counter < SIZE_MAX-8;counter += 8) {
873 ge_p1p1 sum;
875 if (unlikely(endwork))
876 goto end;
878 DOFILTER(i,pk,{
879 if (numwords > 1) {
880 shiftpk(wpk,pk,filter_len(i));
881 size_t j;
882 for (int w = 1;;) {
883 DOFILTER(j,wpk,goto secondfind);
884 goto next;
885 secondfind:
886 if (++w >= numwords)
887 break;
888 shiftpk(wpk,wpk,filter_len(j));
891 // found!
892 // update secret key with counter
893 addsztoscalar32(sk,counter);
894 // sanity check
895 if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31])
896 goto initseed;
898 ADDNUMSUCCESS;
900 // calc checksum
901 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
902 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
903 // version byte
904 pk[PUBLIC_LEN + 2] = 0x03;
905 // full name
906 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
907 onionready(sname,secret,pubonion.raw);
908 pk[PUBLIC_LEN] = 0;
909 // don't reuse same seed
910 goto initseed;
912 next:
913 ge_add(&sum, &ge_public,&ge_eightpoint);
914 ge_p1p1_to_p3(&ge_public,&sum);
915 ge_p3_tobytes(pk,&ge_public);
916 #ifdef STATISTICS
917 ++st->numcalc.v;
918 #endif
920 goto initseed;
922 end:
923 free(sname);
924 return 0;
927 void printhelp(const char *progname)
929 fprintf(stderr,
930 "Usage: %s filter [filter...] [options]\n"
931 " %s -f filterfile [options]\n"
932 "Options:\n"
933 "\t-h - print help\n"
934 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
935 "\t-q - do not print diagnostic output to stderr\n"
936 "\t-x - do not print onion names\n"
937 "\t-o filename - output onion names to specified file\n"
938 "\t-F - include directory names in onion names output\n"
939 "\t-d dirname - output directory\n"
940 "\t-t numthreads - specify number of threads (default - auto)\n"
941 "\t-j numthreads - same as -t\n"
942 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
943 "\t-N numwords - specify number of words per key (default - 1)\n"
944 "\t-z - use faster key generation method. this is now default\n"
945 "\t-Z - use slower key generation method\n"
946 "\t-s - print statistics each 10 seconds\n"
947 "\t-S t - print statistics every specified ammount of seconds\n"
948 "\t-T - do not reset statistics counters when printing\n"
949 ,progname,progname);
950 exit(1);
953 void setworkdir(const char *wd)
955 free(workdir);
956 size_t l = strlen(wd);
957 if (!l) {
958 workdir = 0;
959 workdirlen = 0;
960 if (!quietflag)
961 fprintf(stderr, "unset workdir\n");
962 return;
964 int needslash = 0;
965 if (wd[l-1] != '/')
966 needslash = 1;
967 char *s = malloc(l + needslash + 1);
968 if (!s)
969 abort();
970 memcpy(s, wd, l);
971 if (needslash)
972 s[l++] = '/';
973 s[l] = 0;
975 workdir = s;
976 workdirlen = l;
977 if (!quietflag)
978 fprintf(stderr,"set workdir: %s\n",workdir);
981 VEC_STRUCT(threadvec, pthread_t);
983 int main(int argc,char **argv)
985 char *outfile = 0;
986 const char *arg;
987 int ignoreargs = 0;
988 int dirnameflag = 0;
989 int numthreads = 0;
990 int fastkeygen = 1;
991 struct threadvec threads;
992 #ifdef STATISTICS
993 struct statsvec stats;
994 struct tstatsvec tstats;
995 u64 reportdelay = 0;
996 int realtimestats = 1;
997 #endif
998 int tret;
1000 ge_initeightpoint();
1001 filters_init();
1003 fout = stdout;
1004 pthread_mutex_init(&keysgenerated_mutex, 0);
1005 pthread_mutex_init(&fout_mutex, 0);
1007 const char *progname = argv[0];
1008 if (argc <= 1)
1009 printhelp(progname);
1010 argc--, argv++;
1012 while (argc--) {
1013 arg = *argv++;
1014 if (!ignoreargs && *arg == '-') {
1015 int numargit = 0;
1016 nextarg:
1017 ++arg;
1018 ++numargit;
1019 if (*arg == '-') {
1020 if (numargit > 1) {
1021 fprintf(stderr, "unrecognised argument: -\n");
1022 exit(1);
1024 ++arg;
1025 if (!*arg)
1026 ignoreargs = 1;
1027 else if (!strcmp(arg, "help"))
1028 printhelp(progname);
1029 else {
1030 fprintf(stderr, "unrecognised argument: --%s\n", arg);
1031 exit(1);
1033 numargit = 0;
1035 else if (*arg == 0) {
1036 if (numargit == 1)
1037 ignoreargs = 1;
1038 continue;
1040 else if (*arg == 'h')
1041 printhelp(progname);
1042 else if (*arg == 'f') {
1043 if (argc--)
1044 loadfilterfile(*argv++);
1045 else {
1046 fprintf(stderr, "additional argument required\n");
1047 exit(1);
1050 else if (*arg == 'q')
1051 ++quietflag;
1052 else if (*arg == 'x')
1053 fout = 0;
1054 else if (*arg == 'o') {
1055 if (argc--)
1056 outfile = *argv++;
1057 else {
1058 fprintf(stderr, "additional argument required\n");
1059 exit(1);
1062 else if (*arg == 'F')
1063 dirnameflag = 1;
1064 else if (*arg == 'd') {
1065 if (argc--) {
1066 setworkdir(*argv++);
1068 else {
1069 fprintf(stderr, "additional argument required\n");
1072 else if (*arg == 't' || *arg == 'j') {
1073 if (argc--)
1074 numthreads = atoi(*argv++);
1075 else {
1076 fprintf(stderr, "additional argument required\n");
1077 exit(1);
1080 else if (*arg == 'n') {
1081 if (argc--)
1082 numneedgenerate = (size_t)atoll(*argv++);
1083 else {
1084 fprintf(stderr, "additional argument required\n");
1085 exit(1);
1088 else if (*arg == 'N') {
1089 if (argc--)
1090 numwords = atoi(*argv++);
1091 else {
1092 fprintf(stderr, "additional argument required\n");
1093 exit(1);
1096 else if (*arg == 'Z')
1097 fastkeygen = 0;
1098 else if (*arg == 'z')
1099 fastkeygen = 1;
1100 else if (*arg == 's') {
1101 #ifdef STATISTICS
1102 reportdelay = 10000000;
1103 #else
1104 fprintf(stderr,"statistics support not compiled in\n");
1105 exit(1);
1106 #endif
1108 else if (*arg == 'S') {
1109 #ifdef STATISTICS
1110 if (argc--)
1111 reportdelay = (u64)atoll(*argv++) * 1000000;
1112 else {
1113 fprintf(stderr, "additional argument required\n");
1114 exit(1);
1116 #else
1117 fprintf(stderr,"statistics support not compiled in\n");
1118 exit(1);
1119 #endif
1121 else if (*arg == 'T') {
1122 #ifdef STATISTICS
1123 realtimestats = 0;
1124 #else
1125 fprintf(stderr,"statistics support not compiled in\n");
1126 exit(1);
1127 #endif
1129 else {
1130 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
1131 exit(1);
1133 if (numargit)
1134 goto nextarg;
1136 else filters_add(arg);
1139 filters_prepare();
1141 filters_print();
1143 #ifdef STATISTICS
1144 if (!filters_count() && !reportdelay)
1145 #else
1146 if (!filters_count())
1147 #endif
1148 return 0;
1150 if (outfile)
1151 fout = fopen(outfile, "w");
1153 if (workdir)
1154 createdir(workdir,1);
1156 direndpos = workdirlen;
1157 onionendpos = workdirlen + ONIONLEN;
1159 if (!dirnameflag) {
1160 printstartpos = direndpos;
1161 printlen = ONIONLEN + 1;
1162 } else {
1163 printstartpos = 0;
1164 printlen = onionendpos + 1;
1167 if (numthreads <= 0) {
1168 numthreads = cpucount();
1169 if (numthreads <= 0)
1170 numthreads = 1;
1173 signal(SIGTERM,termhandler);
1174 signal(SIGINT,termhandler);
1176 VEC_INIT(threads);
1177 VEC_ADDN(threads,numthreads);
1178 #ifdef STATISTICS
1179 VEC_INIT(stats);
1180 VEC_ADDN(stats,numthreads);
1181 VEC_ZERO(stats);
1182 VEC_INIT(tstats);
1183 VEC_ADDN(tstats,numthreads);
1184 VEC_ZERO(tstats);
1185 #endif
1187 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
1188 void *tp = 0;
1189 #ifdef STATISTICS
1190 tp = &VEC_BUF(stats,i);
1191 #endif
1192 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
1193 if (tret) {
1194 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
1195 exit(1);
1199 #ifdef STATISTICS
1200 struct timespec nowtime;
1201 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
1202 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
1203 fprintf(stderr, "failed to get time\n");
1204 exit(1);
1206 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1207 #endif
1208 struct timespec ts;
1209 memset(&ts,0,sizeof(ts));
1210 ts.tv_nsec = 100000000;
1211 while (!endwork) {
1212 if (numneedgenerate && keysgenerated >= numneedgenerate) {
1213 endwork = 1;
1214 break;
1216 nanosleep(&ts,0);
1218 #ifdef STATISTICS
1219 clock_gettime(CLOCK_MONOTONIC,&nowtime);
1220 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1221 u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
1222 for (size_t i = 0;i < numthreads;++i) {
1223 u32 newt,tdiff;
1224 // numcalc
1225 newt = VEC_BUF(stats,i).numcalc.v;
1226 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
1227 VEC_BUF(tstats,i).oldnumcalc = newt;
1228 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
1229 sumcalc += VEC_BUF(tstats,i).numcalc;
1230 // numsuccess
1231 newt = VEC_BUF(stats,i).numsuccess.v;
1232 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
1233 VEC_BUF(tstats,i).oldnumsuccess = newt;
1234 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
1235 sumsuccess += VEC_BUF(tstats,i).numsuccess;
1236 // numrestart
1237 newt = VEC_BUF(stats,i).numrestart.v;
1238 tdiff = newt - VEC_BUF(tstats,i).oldnumrestart;
1239 VEC_BUF(tstats,i).oldnumrestart = newt;
1240 VEC_BUF(tstats,i).numrestart += (u64)tdiff;
1241 sumrestart += VEC_BUF(tstats,i).numrestart;
1243 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
1244 if (ireporttime)
1245 ireporttime += reportdelay;
1246 else
1247 ireporttime = inowtime;
1248 if (!ireporttime)
1249 ireporttime = 1;
1251 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
1252 double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
1253 double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime);
1254 fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1255 calcpersec,succpersec,restpersec,
1256 (inowtime - istarttime + elapsedoffset) / 1000000.0);
1258 if (realtimestats) {
1259 for (size_t i = 0;i < numthreads;++i) {
1260 VEC_BUF(tstats,i).numcalc = 0;
1261 VEC_BUF(tstats,i).numsuccess = 0;
1262 VEC_BUF(tstats,i).numrestart = 0;
1264 elapsedoffset += inowtime - istarttime;
1265 istarttime = inowtime;
1268 if (sumcalc > U64_MAX / 2) {
1269 for (size_t i = 0;i < numthreads;++i) {
1270 VEC_BUF(tstats,i).numcalc /= 2;
1271 VEC_BUF(tstats,i).numsuccess /= 2;
1272 VEC_BUF(tstats,i).numrestart /= 2;
1274 u64 timediff = (inowtime - istarttime + 1) / 2;
1275 elapsedoffset += timediff;
1276 istarttime += timediff;
1278 #endif
1281 if (!quietflag)
1282 fprintf(stderr, "waiting for threads to finish...\n");
1283 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
1284 pthread_join(VEC_BUF(threads,i),0);
1285 if (!quietflag)
1286 fprintf(stderr, "done, quitting\n");
1288 pthread_mutex_destroy(&keysgenerated_mutex);
1289 pthread_mutex_destroy(&fout_mutex);
1290 filters_clean();
1292 if (outfile)
1293 fclose(fout);
1295 return 0;