explicit void declaration, remove unused arg. thanks ccomp
[mkp224o.git] / main.c
bloba0c69f864d9cca03ac25be34cf2d3919fbf8228a
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 + sbytes < PUBLIC_LEN;++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 wpk[PUBLIC_LEN] = 0;
755 memset(&pubonion,0,sizeof(pubonion));
756 memcpy(pubonion.raw,pkprefix,pkprefixlen);
757 // write version later as it will be overwritten by hash
758 memcpy(hashsrc,checksumstr,checksumstrlen);
759 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
761 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
762 if (!sname)
763 abort();
764 if (workdir)
765 memcpy(sname,workdir,workdirlen);
767 initseed:
768 randombytes(seed,sizeof(seed));
769 #ifdef STATISTICS
770 ++st->numrestart.v;
771 #endif
773 again:
774 if (unlikely(endwork))
775 goto end;
777 ed25519_seckey_expand(sk,seed);
778 ed25519_pubkey(pk,sk);
780 #ifdef STATISTICS
781 ++st->numcalc.v;
782 #endif
784 DOFILTER(i,pk,{
785 if (numwords > 1) {
786 shiftpk(wpk,pk,filter_len(i));
787 size_t j;
788 for (int w = 1;;) {
789 DOFILTER(j,wpk,goto secondfind);
790 goto next;
791 secondfind:
792 if (++w >= numwords)
793 break;
794 shiftpk(wpk,wpk,filter_len(j));
798 ADDNUMSUCCESS;
800 // calc checksum
801 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
802 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
803 // version byte
804 pk[PUBLIC_LEN + 2] = 0x03;
805 // base32
806 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
807 onionready(sname, secret, pubonion.raw);
808 pk[PUBLIC_LEN] = 0;
809 goto initseed;
811 next:
812 addseed(seed);
813 goto again;
815 end:
816 free(sname);
817 return 0;
820 static void addsztoscalar32(u8 *dst,size_t v)
822 int i;
823 u32 c = 0;
824 for (i = 0;i < 32;++i) {
825 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
826 v >>= 8;
827 ++dst;
831 static void *dofastwork(void *task)
833 union pubonionunion {
834 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
835 struct {
836 u64 prefix[4];
837 u64 key[4];
838 u64 hash[4];
839 } i;
840 } pubonion;
841 u8 * const pk = &pubonion.raw[pkprefixlen];
842 u8 secret[skprefixlen + SECRET_LEN];
843 u8 * const sk = &secret[skprefixlen];
844 u8 seed[SEED_LEN];
845 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
846 u8 wpk[PUBLIC_LEN + 1];
847 ge_p3 ge_public;
848 size_t counter;
849 size_t i;
850 char *sname;
851 #ifdef STATISTICS
852 struct statstruct *st = (struct statstruct *)task;
853 #endif
855 memcpy(secret, skprefix, skprefixlen);
856 wpk[PUBLIC_LEN] = 0;
857 memset(&pubonion,0,sizeof(pubonion));
858 memcpy(pubonion.raw, pkprefix, pkprefixlen);
859 // write version later as it will be overwritten by hash
860 memcpy(hashsrc, checksumstr, checksumstrlen);
861 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
863 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
864 if (!sname)
865 abort();
866 if (workdir)
867 memcpy(sname, workdir, workdirlen);
869 initseed:
870 #ifdef STATISTICS
871 ++st->numrestart.v;
872 #endif
873 randombytes(seed,sizeof(seed));
874 ed25519_seckey_expand(sk,seed);
876 ge_scalarmult_base(&ge_public,sk);
877 ge_p3_tobytes(pk,&ge_public);
879 for (counter = 0;counter < SIZE_MAX-8;counter += 8) {
880 ge_p1p1 sum;
882 if (unlikely(endwork))
883 goto end;
885 DOFILTER(i,pk,{
886 if (numwords > 1) {
887 shiftpk(wpk,pk,filter_len(i));
888 size_t j;
889 for (int w = 1;;) {
890 DOFILTER(j,wpk,goto secondfind);
891 goto next;
892 secondfind:
893 if (++w >= numwords)
894 break;
895 shiftpk(wpk,wpk,filter_len(j));
898 // found!
899 // update secret key with counter
900 addsztoscalar32(sk,counter);
901 // sanity check
902 if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
903 /* These operations should be a no-op. */
904 sk[0] &= 248;
905 sk[31] &= 63;
906 sk[31] |= 64;
908 else
909 goto initseed;
911 ADDNUMSUCCESS;
913 // calc checksum
914 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
915 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
916 // version byte
917 pk[PUBLIC_LEN + 2] = 0x03;
918 // full name
919 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
920 onionready(sname,secret,pubonion.raw);
921 pk[PUBLIC_LEN] = 0;
922 // don't reuse same seed
923 goto initseed;
925 next:
926 ge_add(&sum, &ge_public,&ge_eightpoint);
927 ge_p1p1_to_p3(&ge_public,&sum);
928 ge_p3_tobytes(pk,&ge_public);
929 #ifdef STATISTICS
930 ++st->numcalc.v;
931 #endif
933 goto initseed;
935 end:
936 free(sname);
937 return 0;
940 void printhelp(const char *progname)
942 fprintf(stderr,
943 "Usage: %s filter [filter...] [options]\n"
944 " %s -f filterfile [options]\n"
945 "Options:\n"
946 "\t-h - print help\n"
947 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
948 "\t-q - do not print diagnostic output to stderr\n"
949 "\t-x - do not print onion names\n"
950 "\t-o filename - output onion names to specified file\n"
951 "\t-F - include directory names in onion names output\n"
952 "\t-d dirname - output directory\n"
953 "\t-t numthreads - specify number of threads (default - auto)\n"
954 "\t-j numthreads - same as -t\n"
955 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
956 "\t-N numwords - specify number of words per key (default - 1)\n"
957 "\t-z - use faster key generation method. this is now default\n"
958 "\t-Z - use slower key generation method\n"
959 "\t-s - print statistics each 10 seconds\n"
960 "\t-S t - print statistics every specified ammount of seconds\n"
961 "\t-T - do not reset statistics counters when printing\n"
962 ,progname,progname);
963 exit(1);
966 void setworkdir(const char *wd)
968 free(workdir);
969 size_t l = strlen(wd);
970 if (!l) {
971 workdir = 0;
972 workdirlen = 0;
973 if (!quietflag)
974 fprintf(stderr, "unset workdir\n");
975 return;
977 int needslash = 0;
978 if (wd[l-1] != '/')
979 needslash = 1;
980 char *s = malloc(l + needslash + 1);
981 if (!s)
982 abort();
983 memcpy(s, wd, l);
984 if (needslash)
985 s[l++] = '/';
986 s[l] = 0;
988 workdir = s;
989 workdirlen = l;
990 if (!quietflag)
991 fprintf(stderr,"set workdir: %s\n",workdir);
994 VEC_STRUCT(threadvec, pthread_t);
996 int main(int argc,char **argv)
998 char *outfile = 0;
999 const char *arg;
1000 int ignoreargs = 0;
1001 int dirnameflag = 0;
1002 int numthreads = 0;
1003 int fastkeygen = 1;
1004 struct threadvec threads;
1005 #ifdef STATISTICS
1006 struct statsvec stats;
1007 struct tstatsvec tstats;
1008 u64 reportdelay = 0;
1009 int realtimestats = 1;
1010 #endif
1011 int tret;
1013 ge_initeightpoint();
1014 filters_init();
1016 fout = stdout;
1017 pthread_mutex_init(&keysgenerated_mutex, 0);
1018 pthread_mutex_init(&fout_mutex, 0);
1020 const char *progname = argv[0];
1021 if (argc <= 1)
1022 printhelp(progname);
1023 argc--, argv++;
1025 while (argc--) {
1026 arg = *argv++;
1027 if (!ignoreargs && *arg == '-') {
1028 int numargit = 0;
1029 nextarg:
1030 ++arg;
1031 ++numargit;
1032 if (*arg == '-') {
1033 if (numargit > 1) {
1034 fprintf(stderr, "unrecognised argument: -\n");
1035 exit(1);
1037 ++arg;
1038 if (!*arg)
1039 ignoreargs = 1;
1040 else if (!strcmp(arg, "help"))
1041 printhelp(progname);
1042 else {
1043 fprintf(stderr, "unrecognised argument: --%s\n", arg);
1044 exit(1);
1046 numargit = 0;
1048 else if (*arg == 0) {
1049 if (numargit == 1)
1050 ignoreargs = 1;
1051 continue;
1053 else if (*arg == 'h')
1054 printhelp(progname);
1055 else if (*arg == 'f') {
1056 if (argc--)
1057 loadfilterfile(*argv++);
1058 else {
1059 fprintf(stderr, "additional argument required\n");
1060 exit(1);
1063 else if (*arg == 'q')
1064 ++quietflag;
1065 else if (*arg == 'x')
1066 fout = 0;
1067 else if (*arg == 'o') {
1068 if (argc--)
1069 outfile = *argv++;
1070 else {
1071 fprintf(stderr, "additional argument required\n");
1072 exit(1);
1075 else if (*arg == 'F')
1076 dirnameflag = 1;
1077 else if (*arg == 'd') {
1078 if (argc--) {
1079 setworkdir(*argv++);
1081 else {
1082 fprintf(stderr, "additional argument required\n");
1085 else if (*arg == 't' || *arg == 'j') {
1086 if (argc--)
1087 numthreads = atoi(*argv++);
1088 else {
1089 fprintf(stderr, "additional argument required\n");
1090 exit(1);
1093 else if (*arg == 'n') {
1094 if (argc--)
1095 numneedgenerate = (size_t)atoll(*argv++);
1096 else {
1097 fprintf(stderr, "additional argument required\n");
1098 exit(1);
1101 else if (*arg == 'N') {
1102 if (argc--)
1103 numwords = atoi(*argv++);
1104 else {
1105 fprintf(stderr, "additional argument required\n");
1106 exit(1);
1109 else if (*arg == 'Z')
1110 fastkeygen = 0;
1111 else if (*arg == 'z')
1112 fastkeygen = 1;
1113 else if (*arg == 's') {
1114 #ifdef STATISTICS
1115 reportdelay = 10000000;
1116 #else
1117 fprintf(stderr,"statistics support not compiled in\n");
1118 exit(1);
1119 #endif
1121 else if (*arg == 'S') {
1122 #ifdef STATISTICS
1123 if (argc--)
1124 reportdelay = (u64)atoll(*argv++) * 1000000;
1125 else {
1126 fprintf(stderr, "additional argument required\n");
1127 exit(1);
1129 #else
1130 fprintf(stderr,"statistics support not compiled in\n");
1131 exit(1);
1132 #endif
1134 else if (*arg == 'T') {
1135 #ifdef STATISTICS
1136 realtimestats = 0;
1137 #else
1138 fprintf(stderr,"statistics support not compiled in\n");
1139 exit(1);
1140 #endif
1142 else {
1143 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
1144 exit(1);
1146 if (numargit)
1147 goto nextarg;
1149 else filters_add(arg);
1152 filters_prepare();
1154 filters_print();
1156 #ifdef STATISTICS
1157 if (!filters_count() && !reportdelay)
1158 #else
1159 if (!filters_count())
1160 #endif
1161 return 0;
1163 if (outfile)
1164 fout = fopen(outfile, "w");
1166 if (workdir)
1167 mkdir(workdir, 0700);
1169 direndpos = workdirlen;
1170 onionendpos = workdirlen + ONIONLEN;
1172 if (!dirnameflag) {
1173 printstartpos = direndpos;
1174 printlen = ONIONLEN + 1;
1175 } else {
1176 printstartpos = 0;
1177 printlen = onionendpos + 1;
1180 if (numthreads <= 0) {
1181 numthreads = cpucount();
1182 if (numthreads <= 0)
1183 numthreads = 1;
1186 signal(SIGTERM,termhandler);
1187 signal(SIGINT,termhandler);
1189 VEC_INIT(threads);
1190 VEC_ADDN(threads,numthreads);
1191 #ifdef STATISTICS
1192 VEC_INIT(stats);
1193 VEC_ADDN(stats,numthreads);
1194 VEC_ZERO(stats);
1195 VEC_INIT(tstats);
1196 VEC_ADDN(tstats,numthreads);
1197 VEC_ZERO(tstats);
1198 #endif
1200 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
1201 void *tp = 0;
1202 #ifdef STATISTICS
1203 tp = &VEC_BUF(stats,i);
1204 #endif
1205 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
1206 if (tret) {
1207 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
1208 exit(1);
1212 #ifdef STATISTICS
1213 struct timespec nowtime;
1214 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
1215 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
1216 fprintf(stderr, "failed to get time\n");
1217 exit(1);
1219 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1220 #endif
1221 struct timespec ts;
1222 memset(&ts,0,sizeof(ts));
1223 ts.tv_nsec = 100000000;
1224 while (!endwork) {
1225 if (numneedgenerate && keysgenerated >= numneedgenerate) {
1226 endwork = 1;
1227 break;
1229 nanosleep(&ts,0);
1231 #ifdef STATISTICS
1232 clock_gettime(CLOCK_MONOTONIC,&nowtime);
1233 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1234 u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
1235 for (size_t i = 0;i < numthreads;++i) {
1236 u32 newt,tdiff;
1237 // numcalc
1238 newt = VEC_BUF(stats,i).numcalc.v;
1239 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
1240 VEC_BUF(tstats,i).oldnumcalc = newt;
1241 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
1242 sumcalc += VEC_BUF(tstats,i).numcalc;
1243 // numsuccess
1244 newt = VEC_BUF(stats,i).numsuccess.v;
1245 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
1246 VEC_BUF(tstats,i).oldnumsuccess = newt;
1247 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
1248 sumsuccess += VEC_BUF(tstats,i).numsuccess;
1249 // numrestart
1250 newt = VEC_BUF(stats,i).numrestart.v;
1251 tdiff = newt - VEC_BUF(tstats,i).oldnumrestart;
1252 VEC_BUF(tstats,i).oldnumrestart = newt;
1253 VEC_BUF(tstats,i).numrestart += (u64)tdiff;
1254 sumrestart += VEC_BUF(tstats,i).numrestart;
1256 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
1257 if (ireporttime)
1258 ireporttime += reportdelay;
1259 else
1260 ireporttime = inowtime;
1261 if (!ireporttime)
1262 ireporttime = 1;
1264 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
1265 double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
1266 double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime);
1267 fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1268 calcpersec,succpersec,restpersec,
1269 (inowtime - istarttime + elapsedoffset) / 1000000.0);
1271 if (realtimestats) {
1272 for (size_t i = 0;i < numthreads;++i) {
1273 VEC_BUF(tstats,i).numcalc = 0;
1274 VEC_BUF(tstats,i).numsuccess = 0;
1275 VEC_BUF(tstats,i).numrestart = 0;
1277 elapsedoffset += inowtime - istarttime;
1278 istarttime = inowtime;
1281 if (sumcalc > U64_MAX / 2) {
1282 for (size_t i = 0;i < numthreads;++i) {
1283 VEC_BUF(tstats,i).numcalc /= 2;
1284 VEC_BUF(tstats,i).numsuccess /= 2;
1285 VEC_BUF(tstats,i).numrestart /= 2;
1287 u64 timediff = (inowtime - istarttime + 1) / 2;
1288 elapsedoffset += timediff;
1289 istarttime += timediff;
1291 #endif
1294 if (!quietflag)
1295 fprintf(stderr, "waiting for threads to finish...\n");
1296 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
1297 pthread_join(VEC_BUF(threads,i),0);
1298 if (!quietflag)
1299 fprintf(stderr, "done, quitting\n");
1301 pthread_mutex_destroy(&keysgenerated_mutex);
1302 pthread_mutex_destroy(&fout_mutex);
1303 filters_clean();
1305 if (outfile)
1306 fclose(fout);
1308 return 0;