add numwords functionality
[mkp224o.git] / main.c
blob6279903a9123bef696fb454cdb04d4f4c14cb959
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 <sys/stat.h>
13 #include <sodium/randombytes.h>
15 #include "types.h"
16 #include "likely.h"
17 #include "vec.h"
18 #include "base32.h"
19 #include "cpucount.h"
20 #include "keccak.h"
21 #include "ed25519/ed25519.h"
23 // additional leading zero 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 FILE *fh;
651 if (endwork)
652 return;
654 if (numneedgenerate) {
655 pthread_mutex_lock(&keysgenerated_mutex);
656 if (keysgenerated >= numneedgenerate) {
657 pthread_mutex_unlock(&keysgenerated_mutex);
658 return;
662 if (mkdir(sname,0700) != 0) {
663 if (numneedgenerate)
664 pthread_mutex_unlock(&keysgenerated_mutex);
665 return;
668 if (numneedgenerate) {
669 ++keysgenerated;
670 if (keysgenerated >= numneedgenerate)
671 endwork = 1;
672 pthread_mutex_unlock(&keysgenerated_mutex);
675 strcpy(&sname[onionendpos], "/hs_ed25519_secret_key");
676 fh = fopen(sname, "wb");
677 if (fh) {
678 fwrite(secret, skprefixlen + SECRET_LEN, 1, fh);
679 fclose(fh);
682 strcpy(&sname[onionendpos], "/hostname");
683 fh = fopen(sname, "w");
684 if (fh) {
685 sname[onionendpos] = '\n';
686 fwrite(&sname[direndpos], ONIONLEN+1, 1, fh);
687 fclose(fh);
690 strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
691 fh = fopen(sname, "wb");
692 if (fh) {
693 fwrite(pubonion, pkprefixlen + PUBLIC_LEN, 1, fh);
694 fclose(fh);
697 if (fout) {
698 sname[onionendpos] = '\n';
699 pthread_mutex_lock(&fout_mutex);
700 fwrite(&sname[printstartpos], printlen, 1, fout);
701 fflush(fout);
702 pthread_mutex_unlock(&fout_mutex);
706 // little endian inc
707 static void addseed(u8 *seed)
709 register unsigned int c = 1;
710 for (size_t i = 0;i < SEED_LEN;++i) {
711 c = (unsigned int)seed[i] + c; seed[i] = c & 0xFF; c >>= 8;
712 // unsure if needed
713 if (!c) break;
717 // 0123 4567 xxxx --3--> 3456 7xxx
718 // 0123 4567 xxxx --1--> 1234 567x
719 static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits)
721 size_t i,sbytes = sbits / 8;
722 sbits %= 8;
723 for (i = 0;i < PUBLIC_LEN-sbytes;++i) {
724 dst[i] = (src[i+sbytes] << sbits) |
725 (src[i+sbytes+1] >> (8 - sbits));
727 for(;i < PUBLIC_LEN;++i)
728 dst[i] = 0;
731 static void *dowork(void *task)
733 union pubonionunion {
734 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
735 struct {
736 u64 prefix[4];
737 u64 key[4];
738 u64 hash[4];
739 } i;
740 } pubonion;
741 u8 * const pk = &pubonion.raw[pkprefixlen];
742 u8 secret[skprefixlen + SECRET_LEN];
743 u8 * const sk = &secret[skprefixlen];
744 u8 seed[SEED_LEN];
745 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
746 u8 wpk[PUBLIC_LEN + 1];
747 size_t i;
748 char *sname;
749 #ifdef STATISTICS
750 struct statstruct *st = (struct statstruct *)task;
751 #endif
753 memcpy(secret,skprefix,skprefixlen);
754 memset(&pubonion,0,sizeof(pubonion));
755 memcpy(pubonion.raw,pkprefix,pkprefixlen);
756 // write version later as it will be overwritten by hash
757 memcpy(hashsrc,checksumstr,checksumstrlen);
758 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
760 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
761 if (!sname)
762 abort();
763 if (workdir)
764 memcpy(sname,workdir,workdirlen);
766 initseed:
767 randombytes(seed,sizeof(seed));
768 #ifdef STATISTICS
769 ++st->numrestart.v;
770 #endif
772 again:
773 if (unlikely(endwork))
774 goto end;
776 ed25519_seckey_expand(sk,seed);
777 ed25519_pubkey(pk,sk);
779 #ifdef STATISTICS
780 ++st->numcalc.v;
781 #endif
783 DOFILTER(i,pk,{
784 if (numwords > 1) {
785 shiftpk(wpk,pk,filter_len(i));
786 size_t j;
787 for (int w = 1;;) {
788 DOFILTER(j,wpk,goto secondfind);
789 goto again;
790 secondfind:
791 if (++w >= numwords)
792 break;
793 shiftpk(wpk,wpk,filter_len(j));
797 ADDNUMSUCCESS;
799 // calc checksum
800 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
801 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
802 // version byte
803 pk[PUBLIC_LEN + 2] = 0x03;
804 // base32
805 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
806 onionready(sname, secret, pubonion.raw);
807 pubonion.i.hash[0] = 0;
808 goto initseed;
810 addseed(seed);
811 goto again;
813 end:
814 free(sname);
815 return 0;
818 static void addu64toscalar32(u8 *dst,u64 v)
820 int i;
821 u32 c = 0;
822 for (i = 0;i < 32;++i) {
823 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
824 v >>= 8;
825 ++dst;
829 static void *dofastwork(void *task)
831 union pubonionunion {
832 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
833 struct {
834 u64 prefix[4];
835 u64 key[4];
836 u64 hash[4];
837 } i;
838 } pubonion;
839 u8 * const pk = &pubonion.raw[pkprefixlen];
840 u8 secret[skprefixlen + SECRET_LEN];
841 u8 * const sk = &secret[skprefixlen];
842 u8 seed[SEED_LEN];
843 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
844 u8 wpk[PUBLIC_LEN + 1];
845 ge_p3 ge_public;
846 size_t counter;
847 size_t i;
848 char *sname;
849 #ifdef STATISTICS
850 struct statstruct *st = (struct statstruct *)task;
851 #endif
853 memcpy(secret, skprefix, skprefixlen);
854 memset(&pubonion,0,sizeof(pubonion));
855 memcpy(pubonion.raw, pkprefix, pkprefixlen);
856 // write version later as it will be overwritten by hash
857 memcpy(hashsrc, checksumstr, checksumstrlen);
858 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
860 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
861 if (!sname)
862 abort();
863 if (workdir)
864 memcpy(sname, workdir, workdirlen);
866 initseed:
867 #ifdef STATISTICS
868 ++st->numrestart.v;
869 #endif
870 randombytes(seed,sizeof(seed));
871 ed25519_seckey_expand(sk,seed);
873 ge_scalarmult_base(&ge_public,sk);
874 ge_p3_tobytes(pk,&ge_public);
876 for (counter = 0;counter < SIZE_MAX-8;counter += 8) {
877 ge_p1p1 sum;
879 if (unlikely(endwork))
880 goto end;
882 DOFILTER(i,pk,{
883 if (numwords > 1) {
884 //printf("numwords=%d,filter_len=%d\n",numwords,(int)filter_len(i));
885 shiftpk(wpk,pk,filter_len(i));
886 size_t j;
887 for (int w = 1;;) {
888 DOFILTER(j,wpk,goto secondfind);
889 goto again;
890 secondfind:
891 if (++w >= numwords)
892 break;
893 shiftpk(wpk,wpk,filter_len(j));
896 // found!
897 // update secret key with counter
898 addu64toscalar32(sk,counter);
899 // sanity check
900 if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
901 /* These operations should be a no-op. */
902 sk[0] &= 248;
903 sk[31] &= 63;
904 sk[31] |= 64;
906 else
907 goto initseed;
909 ADDNUMSUCCESS;
911 // calc checksum
912 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
913 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
914 // version byte
915 pk[PUBLIC_LEN + 2] = 0x03;
916 // full name
917 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
918 onionready(sname,secret,pubonion.raw);
919 pubonion.i.hash[0] = 0;
920 // don't reuse same seed
921 goto initseed;
923 again:
924 // next
925 ge_add(&sum, &ge_public,&ge_eightpoint);
926 ge_p1p1_to_p3(&ge_public,&sum);
927 ge_p3_tobytes(pk,&ge_public);
928 #ifdef STATISTICS
929 ++st->numcalc.v;
930 #endif
932 goto initseed;
934 end:
935 free(sname);
936 return 0;
939 void printhelp(const char *progname)
941 fprintf(stderr,
942 "Usage: %s filter [filter...] [options]\n"
943 " %s -f filterfile [options]\n"
944 "Options:\n"
945 "\t-h - print help\n"
946 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
947 "\t-q - do not print diagnostic output to stderr\n"
948 "\t-x - do not print onion names\n"
949 "\t-o filename - output onion names to specified file\n"
950 "\t-F - include directory names in onion names output\n"
951 "\t-d dirname - output directory\n"
952 "\t-t numthreads - specify number of threads (default - auto)\n"
953 "\t-j numthreads - same as -t\n"
954 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
955 "\t-N numwords - specify number of words per key (default - 1)\n"
956 "\t-z - use faster key generation method. this is now default\n"
957 "\t-Z - use slower key generation method\n"
958 "\t-s - print statistics each 10 seconds\n"
959 "\t-S t - print statistics every specified ammount of seconds\n"
960 "\t-T - do not reset statistics counters when printing\n"
961 ,progname,progname);
962 exit(1);
965 void setworkdir(const char *wd)
967 free(workdir);
968 size_t l = strlen(wd);
969 if (!l) {
970 workdir = 0;
971 workdirlen = 0;
972 if (!quietflag)
973 fprintf(stderr, "unset workdir\n");
974 return;
976 int needslash = 0;
977 if (wd[l-1] != '/')
978 needslash = 1;
979 char *s = malloc(l + needslash + 1);
980 if (!s)
981 abort();
982 memcpy(s, wd, l);
983 if (needslash)
984 s[l++] = '/';
985 s[l] = 0;
987 workdir = s;
988 workdirlen = l;
989 if (!quietflag)
990 fprintf(stderr,"set workdir: %s\n",workdir);
993 VEC_STRUCT(threadvec, pthread_t);
995 int main(int argc,char **argv)
997 char *outfile = 0;
998 const char *arg;
999 int ignoreargs = 0;
1000 int dirnameflag = 0;
1001 int numthreads = 0;
1002 int fastkeygen = 1;
1003 struct threadvec threads;
1004 #ifdef STATISTICS
1005 struct statsvec stats;
1006 struct tstatsvec tstats;
1007 u64 reportdelay = 0;
1008 int realtimestats = 1;
1009 #endif
1010 int tret;
1012 ge_initeightpoint();
1013 filters_init();
1015 fout = stdout;
1016 pthread_mutex_init(&keysgenerated_mutex, 0);
1017 pthread_mutex_init(&fout_mutex, 0);
1019 const char *progname = argv[0];
1020 if (argc <= 1)
1021 printhelp(progname);
1022 argc--, argv++;
1024 while (argc--) {
1025 arg = *argv++;
1026 if (!ignoreargs && *arg == '-') {
1027 int numargit = 0;
1028 nextarg:
1029 ++arg;
1030 ++numargit;
1031 if (*arg == '-') {
1032 if (numargit > 1) {
1033 fprintf(stderr, "unrecognised argument: -\n");
1034 exit(1);
1036 ++arg;
1037 if (!*arg)
1038 ignoreargs = 1;
1039 else if (!strcmp(arg, "help"))
1040 printhelp(progname);
1041 else {
1042 fprintf(stderr, "unrecognised argument: --%s\n", arg);
1043 exit(1);
1045 numargit = 0;
1047 else if (*arg == 0) {
1048 if (numargit == 1)
1049 ignoreargs = 1;
1050 continue;
1052 else if (*arg == 'h')
1053 printhelp(progname);
1054 else if (*arg == 'f') {
1055 if (argc--)
1056 loadfilterfile(*argv++);
1057 else {
1058 fprintf(stderr, "additional argument required\n");
1059 exit(1);
1062 else if (*arg == 'q')
1063 ++quietflag;
1064 else if (*arg == 'x')
1065 fout = 0;
1066 else if (*arg == 'o') {
1067 if (argc--)
1068 outfile = *argv++;
1069 else {
1070 fprintf(stderr, "additional argument required\n");
1071 exit(1);
1074 else if (*arg == 'F')
1075 dirnameflag = 1;
1076 else if (*arg == 'd') {
1077 if (argc--) {
1078 setworkdir(*argv++);
1080 else {
1081 fprintf(stderr, "additional argument required\n");
1084 else if (*arg == 't' || *arg == 'j') {
1085 if (argc--)
1086 numthreads = atoi(*argv++);
1087 else {
1088 fprintf(stderr, "additional argument required\n");
1089 exit(1);
1092 else if (*arg == 'n') {
1093 if (argc--)
1094 numneedgenerate = (size_t)atoll(*argv++);
1095 else {
1096 fprintf(stderr, "additional argument required\n");
1097 exit(1);
1100 else if (*arg == 'N') {
1101 if (argc--)
1102 numwords = atoi(*argv++);
1103 else {
1104 fprintf(stderr, "additional argument required\n");
1105 exit(1);
1108 else if (*arg == 'Z')
1109 fastkeygen = 0;
1110 else if (*arg == 'z')
1111 fastkeygen = 1;
1112 else if (*arg == 's') {
1113 #ifdef STATISTICS
1114 reportdelay = 10000000;
1115 #else
1116 fprintf(stderr,"statistics support not compiled in\n");
1117 exit(1);
1118 #endif
1120 else if (*arg == 'S') {
1121 #ifdef STATISTICS
1122 if (argc--)
1123 reportdelay = (u64)atoll(*argv++) * 1000000;
1124 else {
1125 fprintf(stderr, "additional argument required\n");
1126 exit(1);
1128 #else
1129 fprintf(stderr,"statistics support not compiled in\n");
1130 exit(1);
1131 #endif
1133 else if (*arg == 'T') {
1134 #ifdef STATISTICS
1135 realtimestats = 0;
1136 #else
1137 fprintf(stderr,"statistics support not compiled in\n");
1138 exit(1);
1139 #endif
1141 else {
1142 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
1143 exit(1);
1145 if (numargit)
1146 goto nextarg;
1148 else filters_add(arg);
1151 filters_prepare();
1153 filters_print();
1155 #ifdef STATISTICS
1156 if (!filters_count() && !reportdelay)
1157 #else
1158 if (!filters_count())
1159 #endif
1160 return 0;
1162 if (outfile)
1163 fout = fopen(outfile, "w");
1165 if (workdir)
1166 mkdir(workdir, 0700);
1168 direndpos = workdirlen;
1169 onionendpos = workdirlen + ONIONLEN;
1171 if (!dirnameflag) {
1172 printstartpos = direndpos;
1173 printlen = ONIONLEN + 1;
1174 } else {
1175 printstartpos = 0;
1176 printlen = onionendpos + 1;
1179 if (numthreads <= 0) {
1180 numthreads = cpucount();
1181 if (numthreads <= 0)
1182 numthreads = 1;
1185 signal(SIGTERM,termhandler);
1186 signal(SIGINT,termhandler);
1188 VEC_INIT(threads);
1189 VEC_ADDN(threads,numthreads);
1190 #ifdef STATISTICS
1191 VEC_INIT(stats);
1192 VEC_ADDN(stats,numthreads);
1193 VEC_ZERO(stats);
1194 VEC_INIT(tstats);
1195 VEC_ADDN(tstats,numthreads);
1196 VEC_ZERO(tstats);
1197 #endif
1199 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
1200 void *tp = 0;
1201 #ifdef STATISTICS
1202 tp = &VEC_BUF(stats,i);
1203 #endif
1204 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
1205 if (tret) {
1206 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
1207 exit(1);
1211 #ifdef STATISTICS
1212 struct timespec nowtime;
1213 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
1214 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
1215 fprintf(stderr, "failed to get time\n");
1216 exit(1);
1218 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1219 #endif
1220 struct timespec ts;
1221 memset(&ts,0,sizeof(ts));
1222 ts.tv_nsec = 100000000;
1223 while (!endwork) {
1224 if (numneedgenerate && keysgenerated >= numneedgenerate) {
1225 endwork = 1;
1226 break;
1228 nanosleep(&ts,0);
1230 #ifdef STATISTICS
1231 clock_gettime(CLOCK_MONOTONIC,&nowtime);
1232 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1233 u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
1234 for (size_t i = 0;i < numthreads;++i) {
1235 u32 newt,tdiff;
1236 // numcalc
1237 newt = VEC_BUF(stats,i).numcalc.v;
1238 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
1239 VEC_BUF(tstats,i).oldnumcalc = newt;
1240 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
1241 sumcalc += VEC_BUF(tstats,i).numcalc;
1242 // numsuccess
1243 newt = VEC_BUF(stats,i).numsuccess.v;
1244 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
1245 VEC_BUF(tstats,i).oldnumsuccess = newt;
1246 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
1247 sumsuccess += VEC_BUF(tstats,i).numsuccess;
1248 // numrestart
1249 newt = VEC_BUF(stats,i).numrestart.v;
1250 tdiff = newt - VEC_BUF(tstats,i).oldnumrestart;
1251 VEC_BUF(tstats,i).oldnumrestart = newt;
1252 VEC_BUF(tstats,i).numrestart += (u64)tdiff;
1253 sumrestart += VEC_BUF(tstats,i).numrestart;
1255 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
1256 if (ireporttime)
1257 ireporttime += reportdelay;
1258 else
1259 ireporttime = inowtime;
1260 if (!ireporttime)
1261 ireporttime = 1;
1263 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
1264 double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
1265 double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime);
1266 fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1267 calcpersec,succpersec,restpersec,
1268 (inowtime - istarttime + elapsedoffset) / 1000000.0);
1270 if (realtimestats) {
1271 for (size_t i = 0;i < numthreads;++i) {
1272 VEC_BUF(tstats,i).numcalc = 0;
1273 VEC_BUF(tstats,i).numsuccess = 0;
1274 VEC_BUF(tstats,i).numrestart = 0;
1276 elapsedoffset += inowtime - istarttime;
1277 istarttime = inowtime;
1280 if (sumcalc > U64_MAX / 2) {
1281 for (size_t i = 0;i < numthreads;++i) {
1282 VEC_BUF(tstats,i).numcalc /= 2;
1283 VEC_BUF(tstats,i).numsuccess /= 2;
1284 VEC_BUF(tstats,i).numrestart /= 2;
1286 u64 timediff = (inowtime - istarttime + 1) / 2;
1287 elapsedoffset += timediff;
1288 istarttime += timediff;
1290 #endif
1293 if (!quietflag)
1294 fprintf(stderr, "waiting for threads to finish...\n");
1295 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
1296 pthread_join(VEC_BUF(threads,i),0);
1297 if (!quietflag)
1298 fprintf(stderr, "done, quitting\n");
1300 pthread_mutex_destroy(&keysgenerated_mutex);
1301 pthread_mutex_destroy(&fout_mutex);
1302 filters_clean();
1304 if (outfile)
1305 fclose(fout);
1307 return 0;