add .gitignore, further improve vec
[mkp224o.git] / main.c
blob43d3c2eb4a43e3dcb1b41750c1c61e3c7d407adc
1 #ifdef __linux__
2 #define _POSIX_C_SOURCE 200112L
3 #endif
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <stdint.h>
8 #include <string.h>
9 #include <assert.h>
10 #include <time.h>
11 #include <pthread.h>
12 #include <signal.h>
13 #include <sys/stat.h>
14 #include <sodium/randombytes.h>
16 #include "types.h"
17 #include "likely.h"
18 #include "vec.h"
19 #include "base32.h"
20 #include "cpucount.h"
21 #include "keccak.h"
22 #include "ed25519/ed25519.h"
24 // additional leading zero is added by C
25 static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
26 #define pkprefixlen (29 + 3)
27 static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
28 #define skprefixlen (29 + 3)
29 static const char * const checksumstr = ".onion checksum";
30 #define checksumstrlen 15
32 // output directory
33 static char *workdir = 0;
34 static size_t workdirlen = 0;
36 static int quietflag = 0;
38 #define SECRET_LEN 64
39 #define PUBLIC_LEN 32
40 #define SEED_LEN 32
41 // with checksum + version num
42 #define PUBONION_LEN (PUBLIC_LEN + 3)
43 // with newline included
44 #define ONIONLEN 62
46 static size_t onionendpos; // end of .onion within string
47 static size_t direndpos; // end of dir before .onion within string
48 static size_t printstartpos; // where to start printing from
49 static size_t printlen; // precalculated, related to printstartpos
51 static pthread_mutex_t fout_mutex;
52 static FILE *fout;
53 static size_t numneedgenerate = 0;
54 static pthread_mutex_t keysgenerated_mutex;
55 static volatile size_t keysgenerated = 0;
56 static volatile int endwork = 0;
58 static void termhandler(int sig)
60 switch (sig) {
61 case SIGTERM:
62 case SIGINT:
63 endwork = 1;
64 break;
68 // filters stuff
70 struct binfilter {
71 u8 f[PUBLIC_LEN];
72 size_t len; // real len minus one
73 u8 mask;
74 } ;
76 #ifdef INTFILTER
77 #ifndef IFT
78 #define IFT u64
79 #endif
80 struct intfilter {
81 IFT f;
82 #ifndef BINSEARCH
83 IFT m;
84 #endif
85 } ;
86 VEC_STRUCT(ifiltervec,struct intfilter) ifilters;
87 #ifdef BINSEARCH
88 IFT ifiltermask;
89 #endif // BINSEARCH
90 #else // INTFILTER
91 VEC_STRUCT(bfiltervec,struct binfilter) bfilters;
92 #endif // INTFILTER
94 static void filters_init()
96 #ifdef INTFILTER
97 VEC_INIT(ifilters);
98 #else
99 VEC_INIT(bfilters);
100 #endif
103 #ifdef INTFILTER
104 // o - old filter, n - new
105 // return -1 - old stays, 0 - no conflict, 1 - new overrides old
106 // assumes masked bits are cleared already
107 #ifndef BINSEARCH
108 static inline int ifilter_conflict(struct intfilter *o,struct intfilter *n)
110 if ((o->f & n->m) != (n->f & o->m))
111 return 0;
112 // determine which filter contain less bits
113 if (o->m <= n->m)
114 return -1;
115 return 1;
117 #else // BINSEARCH
118 static inline int ifilter_conflict(struct intfilter *o,struct intfilter *n,IFT mask)
120 if ((o->f & mask) != (n->f & ifiltermask))
121 return 0;
122 if (ifiltermask <= mask)
123 return -1;
124 return 1;
126 #endif // BINSEARCH
127 #else // INTFILTER
128 // o - old filter, n - new
129 // return: -1 - old stays, 0 - no conflict, 1 - new overrides old
130 // assumes irrelevant bits are cleared already
131 static inline int bfilter_conflict(struct binfilter *o,struct binfilter *n)
133 for (size_t i = 0;i < sizeof(o->f);++i) {
134 u8 oo,nn;
135 if (i < n->len)
136 oo = o->f[i];
137 else if (i == n->len)
138 oo = o->f[i] & n->mask;
139 else
140 oo = 0;
141 if (i < o->len)
142 nn = n->f[i];
143 else if (i == o->len)
144 nn = n->f[i] & o->mask;
145 else
146 nn = 0;
147 if (oo != nn)
148 return 0;
150 // functional filters subset was the same
151 // determine which filter contain less bits
152 if (o->len < n->len)
153 return -1;
154 if (o->len > n->len)
155 return 1;
156 if (o->mask <= n->mask)
157 return -1;
158 return 1;
160 #endif
162 #ifdef INTFILTER
163 #ifdef BINSEARCH
165 * raw representation -- FF.FF.F0.00
166 * big endian -- 0xFFFFF000
167 * little endian -- 0x00F0FFFF
168 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
169 * 0x0000F000 + 1 -> 0x0000F001
170 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
171 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
172 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
173 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
174 * 0x00f00000 + 1 -> 0x00f00001
175 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
176 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
177 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
179 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
180 * 0x0FffFFff + 1 -> 0x10000000
181 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
182 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
183 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
184 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
185 * 0xFFffFF0f + 1 -> 0xFFffFF10
186 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
187 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
188 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
190 * essentially, we have to make direct mask + shifted mask bits worth of information
191 * and then split it into 2 parts
192 * we do not need absolute shifted mask shifting value, just relative to direct mask
193 * 0x0sss00dd - shifted & direct mask combo
194 * 0x000sssdd - combined mask
195 * 8 - relshiftval
196 * generate values from 0x00000000 to 0x000sssdd
197 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
198 * or..
199 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
200 * ...
201 * above method doesn't work in some cases. better way:
202 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
203 * 0x800f0000 >> 16 -> 0x0000800f
204 * 0x0000800f + 1 -> 0x00008010
205 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
206 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
209 // add expanded set of values
210 // space for that must already be allocated
211 static void ifilter_addexpanded(
212 size_t n,struct intfilter *ifltr,
213 IFT dmask,IFT smask,IFT cmask,
214 int ishift,int rshift)
216 for (size_t j = 0;;++j) {
217 VEC_BUF(ifilters,n + j).f =
218 ifltr->f | (((j & dmask) | ((j << rshift) & smask)) << ishift);
219 if (j == cmask)
220 break;
224 // expand existing stuff
225 // allocates needed stuff on its own
226 static void ifilter_expand(IFT dmask,IFT smask,IFT cmask,int ishift,int rshift)
228 size_t len = VEC_LENGTH(ifilters);
229 printf(">expand:cm:%08X,len:%d,cm*len:%d\n",
230 cmask,(int)len,(int)(cmask * len));
231 VEC_ADDN(ifilters,cmask * len);
232 printf(">expand after\n");
233 size_t esz = cmask + 1; // size of expanded elements
234 for (size_t i = len - 1;;--i) {
235 for (IFT j = 0;;++j) {
236 VEC_BUF(ifilters,i * esz + j).f =
237 VEC_BUF(ifilters,i).f |
238 (((j & dmask) | ((j << rshift) & smask)) << ishift);
239 if (j == cmask)
240 break;
242 if (i == 0)
243 break;
247 static inline void ifilter_addflatten(struct intfilter *ifltr,IFT mask)
249 printf(">enter flatten,f:%08X,m:%08X\n",ifltr->f,mask);
250 if (VEC_LENGTH(ifilters) == 0) {
251 printf(">flatten simple\n");
252 // simple
253 VEC_ADD(ifilters,*ifltr);
254 ifiltermask = mask;
255 return;
257 if (ifiltermask == mask) {
258 printf(">flatten lucky\n");
259 // lucky, only need to insert at the right place
260 VEC_FOR(ifilters,i) {
261 if (VEC_BUF(ifilters,i).f > ifltr->f) {
262 VEC_INSERT(ifilters,i,*ifltr);
263 return;
266 VEC_ADD(ifilters,*ifltr);
267 return;
269 printf(">flatten complicated; em:0x%08X,m:0x%08X\n",ifiltermask,mask);
270 IFT cross = ifiltermask ^ mask;
271 printf(">cross:%08X\n",cross);
272 int ishift = 0;
273 while ((cross & 1) == 0) {
274 ++ishift;
275 cross >>= 1;
277 printf(">ishift:%d,cross:%08X\n",ishift,cross);
278 IFT smask = cross & (cross + 1); // shift mask
279 printf(">smask:%08X\n",smask);
280 IFT dmask = cross ^ smask; // direct mask
281 printf(">dmask:%08X\n",dmask);
282 IFT cmask; // combined mask
283 int rshift = 0; // relative shift
284 while (cmask = (smask >> rshift) | dmask,(cmask & (cmask + 1)) != 0)
285 ++rshift;
286 printf(">cmask:%08X,rshift:%d\n",cmask,rshift);
287 // preparations done
288 if (ifiltermask > mask) {
289 // already existing stuff has more precise mask than we
290 // so we need to expand our stuff
291 // first find where we should insert
292 VEC_FOR(ifilters,i) {
293 if (VEC_BUF(ifilters,i).f > ifltr->f) {
294 // there
295 VEC_INSERTN(ifilters,i,cmask + 1);
296 ifilter_addexpanded(i,ifltr,dmask,smask,cmask,ishift,rshift);
297 return;
300 size_t i = VEC_LENGTH(ifilters);
301 VEC_ADDN(ifilters,cmask + 1);
302 ifilter_addexpanded(i,ifltr,dmask,smask,cmask,ishift,rshift);
304 else {
305 // adjust existing mask
306 ifiltermask = mask;
307 // already existing stuff needs to be expanded
308 ifilter_expand(dmask,smask,cmask,ishift,rshift);
309 // now just insert our stuff in the right place
310 VEC_FOR(ifilters,i) {
311 if (VEC_BUF(ifilters,i).f > ifltr->f) {
312 VEC_INSERT(ifilters,i,*ifltr);
313 return;
316 VEC_ADD(ifilters,*ifltr);
319 #endif // BINSEARCH
320 #endif // INTFILTER
322 static void filters_add(const char *filter)
324 struct binfilter bf;
325 size_t ret, ret2;
326 #ifdef INTFILTER
327 union intconv {
328 IFT i;
329 u8 b[sizeof(IFT)];
330 } fc,mc;
331 #endif
333 memset(&bf,0,sizeof(bf));
335 if (!base32_valid(filter,&ret)) {
336 fprintf(stderr, "filter \"%s\" is invalid\n", filter);
337 return;
339 ret = BASE32_FROM_LEN(ret);
340 if (!ret)
341 return;
342 #ifdef INTFILTER
343 if (ret > sizeof(IFT))
344 #else
345 if (ret > PUBLIC_LEN)
346 #endif
348 fprintf(stderr, "filter \"%s\" is too long\n", filter);
349 return;
351 ret2 = base32_from(bf.f,&bf.mask,filter);
352 assert(ret == ret2);
353 bf.len = ret - 1;
354 #ifdef INTFILTER
355 mc.i = 0;
356 for (size_t i = 0;i < bf.len;++i)
357 mc.b[i] = 0xFF;
358 mc.b[bf.len] = bf.mask;
359 memcpy(fc.b,bf.f,sizeof(fc.b));
360 fc.i &= mc.i;
361 struct intfilter ifltr = {
362 .f = fc.i,
363 #ifndef BINSEARCH
364 .m = mc.i,
365 #endif
367 VEC_FOR(ifilters,i) {
368 int c;
369 #ifndef BINSEARCH
370 c = ifilter_conflict(&VEC_BUF(ifilters,i),&ifltr);
371 #else
372 c = ifilter_conflict(&VEC_BUF(ifilters,i),&ifltr,mc.i);
373 #endif
374 if (c < 0)
375 return; // old filter eats us
376 else if (c > 0) {
377 VEC_REMOVE(ifilters,i);
378 --i;
379 // we eat old filter
382 #ifdef BINSEARCH
383 ifilter_addflatten(&ifltr,mc.i);
384 #else
385 VEC_FOR(ifilters,i) {
386 // filter with least bits first
387 if (VEC_BUF(ifilters,i).m > ifltr.m) {
388 VEC_INSERT(ifilters,i,ifltr);
389 return;
392 VEC_ADD(ifilters,ifltr);
393 #endif // BINSEARCH
394 #else // INTFILTER
395 VEC_FOR(bfilters,i) {
396 int c = bfilter_conflict(&VEC_BUF(bfilters,i),&bf);
397 if (c < 0)
398 return; // old filter eats us
399 else if (c > 0) {
400 VEC_REMOVE(bfilters,i);
401 --i;
402 // we eat old filter
405 #ifdef BINSEARCH
406 // TODO
407 #else
408 VEC_FOR(bfilters,i) {
409 // filter with least bits first
410 if (VEC_BUF(bfilters,i).len > bf.len ||
411 (VEC_BUF(bfilters,i).len == bf.len &&
412 (VEC_BUF(bfilters,i).mask > bf.mask)))
414 VEC_INSERT(bfilters,i,bf);
415 return;
418 VEC_ADD(bfilters,bf);
419 #endif // BINSEARCH
420 #endif // INTFILTER
423 static void filters_clean()
425 #ifdef INTFILTER
426 VEC_FREE(ifilters);
427 #else
428 VEC_FREE(bfilters);
429 #endif
432 static size_t filters_count()
434 #ifdef INTFILTER
435 return VEC_LENGTH(ifilters);
436 #else
437 return VEC_LENGTH(bfilters);
438 #endif
441 #ifdef INTFILTER
443 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
444 #ifndef BINSEARCH
445 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
446 #else
447 #define MATCHFILTER(it,pk) ((*(IFT *)(pk) & ifiltermask) == VEC_BUF(ifilters,it).f)
448 #endif
450 #else
452 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
453 #define MATCHFILTER(it,pk) ( \
454 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
455 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
457 #endif
459 static void loadfilterfile(const char *fname)
461 char buf[128];
462 FILE *f = fopen(fname,"r");
463 while (fgets(buf,sizeof(buf),f)) {
464 for (char *p = buf;*p;++p) {
465 if (*p == '\n') {
466 *p = 0;
467 break;
470 if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0)
471 filters_add(buf);
475 static void printfilters()
477 size_t i,l;
478 #ifdef INTFILTER
479 l = VEC_LENGTH(ifilters);
480 #else
481 l = VEC_LENGTH(bfilters);
482 #endif
483 if (l)
484 fprintf(stderr, "filters:\n");
485 else
486 fprintf(stderr, "no filters defined\n");
488 for (i = 0;i < l;++i) {
489 char buf0[256],buf1[256];
490 u8 bufx[128];
491 #ifdef INTFILTER
492 size_t len = 0;
493 u8 *imraw;
494 #ifndef BINSEARCH
495 imraw = (u8 *)&VEC_BUF(ifilters,i).m;
496 #else
497 imraw = (u8 *)&ifiltermask;
498 #endif
499 while (len < sizeof(IFT) && imraw[len] != 0x00) ++len;
500 u8 mask = imraw[len-1];
501 u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f;
502 #else
503 size_t len = VEC_BUF(bfilters,i).len + 1;
504 u8 mask = VEC_BUF(bfilters,i).mask;
505 u8 *ifraw = VEC_BUF(bfilters,i).f;
506 #endif
507 base32_to(buf0,ifraw,len);
508 memcpy(bufx,ifraw,len);
509 bufx[len - 1] |= ~mask;
510 base32_to(buf1,bufx,len);
511 char *a = buf0,*b = buf1;
512 while (*a && *a == *b)
513 ++a, ++b;
514 *a = 0;
515 fprintf(stderr, "\t%s\n",buf0);
519 // statistics, if enabled
520 #ifdef STATISTICS
521 struct statstruct {
522 union {
523 u32 v;
524 size_t align;
525 } numcalc;
526 union {
527 u32 v;
528 size_t align;
529 } numsuccess;
531 VEC_STRUCT(statsvec,struct statstruct);
533 struct tstatstruct {
534 u64 numcalc;
535 u64 numsuccess;
536 u32 oldnumcalc;
537 u32 oldnumsuccess;
539 VEC_STRUCT(tstatsvec,struct tstatstruct);
540 #endif
543 static void onionready(char *sname, const u8 *secret, const u8 *pubonion)
545 FILE *fh;
547 if (endwork)
548 return;
550 if (numneedgenerate) {
551 pthread_mutex_lock(&keysgenerated_mutex);
552 if (keysgenerated >= numneedgenerate) {
553 pthread_mutex_unlock(&keysgenerated_mutex);
554 return;
558 if (mkdir(sname,0700) != 0) {
559 if (numneedgenerate)
560 pthread_mutex_unlock(&keysgenerated_mutex);
561 return;
564 if (numneedgenerate) {
565 ++keysgenerated;
566 if (keysgenerated >= numneedgenerate)
567 endwork = 1;
568 pthread_mutex_unlock(&keysgenerated_mutex);
571 strcpy(&sname[onionendpos], "/hs_ed25519_secret_key");
572 fh = fopen(sname, "wb");
573 if (fh) {
574 fwrite(secret, skprefixlen + SECRET_LEN, 1, fh);
575 fclose(fh);
578 strcpy(&sname[onionendpos], "/hostname");
579 fh = fopen(sname, "w");
580 if (fh) {
581 sname[onionendpos] = '\n';
582 fwrite(&sname[direndpos], ONIONLEN+1, 1, fh);
583 fclose(fh);
586 strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
587 fh = fopen(sname, "wb");
588 if (fh) {
589 fwrite(pubonion, pkprefixlen + PUBLIC_LEN, 1, fh);
590 fclose(fh);
593 if (fout) {
594 sname[onionendpos] = '\n';
595 pthread_mutex_lock(&fout_mutex);
596 fwrite(&sname[printstartpos], printlen, 1, fout);
597 fflush(fout);
598 pthread_mutex_unlock(&fout_mutex);
602 // little endian inc
603 static void addseed(u8 *seed)
605 register unsigned int c = 1;
606 for (size_t i = 0;i < SEED_LEN;++i) {
607 c = (unsigned int)seed[i] + c; seed[i] = c & 0xFF; c >>= 8;
608 // unsure if needed
609 if (!c) break;
613 static void *dowork(void *task)
615 union pubonionunion {
616 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
617 struct {
618 u64 prefix[4];
619 u64 key[4];
620 u64 hash[4];
621 } i;
622 } pubonion;
623 u8 * const pk = &pubonion.raw[pkprefixlen];
624 u8 secret[skprefixlen + SECRET_LEN];
625 u8 * const sk = &secret[skprefixlen];
626 u8 seed[SEED_LEN];
627 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
628 size_t i;
629 char *sname;
630 #ifdef STATISTICS
631 struct statstruct *st = (struct statstruct *)task;
632 #endif
634 memcpy(secret,skprefix,skprefixlen);
635 memcpy(pubonion.raw,pkprefix,pkprefixlen);
636 // write version later as it will be overwritten by hash
637 memcpy(hashsrc,checksumstr,checksumstrlen);
638 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
640 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
641 if (workdir)
642 memcpy(sname,workdir,workdirlen);
644 initseed:
645 randombytes(seed,sizeof(seed));
647 again:
648 if (unlikely(endwork))
649 goto end;
651 ed25519_seckey_expand(sk,seed);
652 ed25519_pubkey(pk,sk);
654 #ifdef STATISTICS
655 ++st->numcalc.v;
656 #endif
658 FILTERFOR(i) {
659 if (unlikely(MATCHFILTER(i,pk))) {
660 #ifdef STATISTICS
661 ++st->numsuccess.v;
662 #endif
663 // calc checksum
664 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
665 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
666 // version byte
667 pk[PUBLIC_LEN + 2] = 0x03;
668 // base32
669 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
670 onionready(sname, secret, pubonion.raw);
671 goto initseed;
674 addseed(seed);
675 goto again;
677 end:
678 free(sname);
679 return 0;
682 static void addu64toscalar32(u8 *dst,u64 v)
684 int i;
685 u32 c = 0;
686 for (i = 0;i < 32;++i) {
687 c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
688 v >>= 8;
689 ++dst;
693 static void *dofastwork(void *task)
695 union pubonionunion {
696 u8 raw[pkprefixlen + PUBLIC_LEN + 32];
697 struct {
698 u64 prefix[4];
699 u64 key[4];
700 u64 hash[4];
701 } i;
702 } pubonion;
703 u8 * const pk = &pubonion.raw[pkprefixlen];
704 u8 secret[skprefixlen + SECRET_LEN];
705 u8 * const sk = &secret[skprefixlen];
706 u8 seed[SEED_LEN];
707 u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
708 ge_p3 ge_public;
709 u64 counter;
710 size_t i;
711 char *sname;
712 #ifdef STATISTICS
713 struct statstruct *st = (struct statstruct *)task;
714 #endif
716 memcpy(secret, skprefix, skprefixlen);
717 memcpy(pubonion.raw, pkprefix, pkprefixlen);
718 // write version later as it will be overwritten by hash
719 memcpy(hashsrc, checksumstr, checksumstrlen);
720 hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
722 sname = malloc(workdirlen + ONIONLEN + 63 + 1);
723 if (workdir)
724 memcpy(sname, workdir, workdirlen);
726 initseed:
727 randombytes(seed,sizeof(seed));
728 ed25519_seckey_expand(sk,seed);
730 ge_scalarmult_base(&ge_public,sk);
731 ge_p3_tobytes(pk,&ge_public);
733 for (counter = 0;counter < U64_MAX-8;counter += 8) {
734 ge_p1p1 sum;
736 if (unlikely(endwork))
737 goto end;
739 FILTERFOR(i) {
740 if (unlikely(MATCHFILTER(i,pk))) {
741 // found!
742 // update secret key with counter
743 addu64toscalar32(sk,counter);
744 // sanity check
745 if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
746 /* These operations should be a no-op. */
747 sk[0] &= 248;
748 sk[31] &= 63;
749 sk[31] |= 64;
751 else goto initseed;
752 #ifdef STATISTICS
753 ++st->numsuccess.v;
754 #endif
755 // calc checksum
756 memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
757 FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
758 // version byte
759 pk[PUBLIC_LEN + 2] = 0x03;
760 // full name
761 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
762 onionready(sname,secret,pubonion.raw);
763 // don't reuse same seed
764 goto initseed;
768 // next
769 ge_add(&sum, &ge_public,&ge_eightpoint);
770 ge_p1p1_to_p3(&ge_public,&sum);
771 ge_p3_tobytes(pk,&ge_public);
772 #ifdef STATISTICS
773 ++st->numcalc.v;
774 #endif
776 goto initseed;
778 end:
779 free(sname);
780 return 0;
783 void printhelp(const char *progname)
785 fprintf(stderr,
786 "Usage: %s filter [filter...] [options]\n"
787 " %s -f filterfile [options]\n"
788 "Options:\n"
789 "\t-h - print help\n"
790 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
791 "\t-q - do not print diagnostic output to stderr\n"
792 "\t-x - do not print onion names\n"
793 "\t-o filename - output onion names to specified file\n"
794 "\t-F - include directory names in onion names output\n"
795 "\t-d dirname - output directory\n"
796 "\t-t numthreads - specify number of threads (default - auto)\n"
797 "\t-j numthreads - same as -t\n"
798 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
799 "\t-z - use faster key generation method. this is now default\n"
800 "\t-Z - use slower key generation method\n"
801 "\t-s - print statistics each 10 seconds\n"
802 "\t-S t - print statistics every specified ammount of seconds\n"
803 "\t-T - do not reset statistics counters when printing\n"
804 ,progname,progname);
805 exit(1);
808 void setworkdir(const char *wd)
810 free(workdir);
811 size_t l = strlen(wd);
812 if (!l) {
813 workdir = 0;
814 workdirlen = 0;
815 if (!quietflag)
816 fprintf(stderr, "unset workdir\n");
817 return;
819 int needslash = 0;
820 if (wd[l-1] != '/')
821 needslash = 1;
822 char *s = malloc(l + needslash + 1);
823 memcpy(s, wd, l);
824 if (needslash)
825 s[l++] = '/';
826 s[l] = 0;
828 workdir = s;
829 workdirlen = l;
830 if (!quietflag)
831 fprintf(stderr,"set workdir: %s\n",workdir);
834 VEC_STRUCT(threadvec, pthread_t);
836 int main(int argc,char **argv)
838 char *outfile = 0;
839 const char *arg;
840 int ignoreargs = 0;
841 int dirnameflag = 0;
842 int numthreads = 0;
843 int fastkeygen = 1;
844 struct threadvec threads;
845 #ifdef STATISTICS
846 struct statsvec stats;
847 struct tstatsvec tstats;
848 u64 reportdelay = 0;
849 int realtimestats = 1;
850 #endif
851 int tret;
853 ge_initeightpoint();
854 filters_init();
856 fout = stdout;
857 pthread_mutex_init(&keysgenerated_mutex, 0);
858 pthread_mutex_init(&fout_mutex, 0);
860 const char *progname = argv[0];
861 if (argc <= 1)
862 printhelp(progname);
863 argc--, argv++;
865 while (argc--) {
866 arg = *argv++;
867 if (!ignoreargs && *arg == '-') {
868 int numargit = 0;
869 nextarg:
870 ++arg;
871 ++numargit;
872 if (*arg == '-') {
873 if (numargit > 1) {
874 fprintf(stderr, "unrecognised argument: -\n");
875 exit(1);
877 ++arg;
878 if (!*arg)
879 ignoreargs = 1;
880 else if (!strcmp(arg, "help"))
881 printhelp(progname);
882 else {
883 fprintf(stderr, "unrecognised argument: --%s\n", arg);
884 exit(1);
886 numargit = 0;
888 else if (*arg == 0) {
889 if (numargit == 1)
890 ignoreargs = 1;
891 continue;
893 else if (*arg == 'h')
894 printhelp(progname);
895 else if (*arg == 'f') {
896 if (argc--)
897 loadfilterfile(*argv++);
898 else {
899 fprintf(stderr, "additional argument required\n");
900 exit(1);
903 else if (*arg == 'q')
904 ++quietflag;
905 else if (*arg == 'x')
906 fout = 0;
907 else if (*arg == 'o') {
908 if (argc--)
909 outfile = *argv++;
910 else {
911 fprintf(stderr, "additional argument required\n");
912 exit(1);
915 else if (*arg == 'F')
916 dirnameflag = 1;
917 else if (*arg == 'd') {
918 if (argc--) {
919 setworkdir(*argv++);
921 else {
922 fprintf(stderr, "additional argument required\n");
925 else if (*arg == 't' || *arg == 'j') {
926 if (argc--)
927 numthreads = atoi(*argv++);
928 else {
929 fprintf(stderr, "additional argument required\n");
930 exit(1);
933 else if (*arg == 'n') {
934 if (argc--)
935 numneedgenerate = (size_t)atoll(*argv++);
936 else {
937 fprintf(stderr, "additional argument required\n");
938 exit(1);
941 else if (*arg == 'Z')
942 fastkeygen = 0;
943 else if (*arg == 'z')
944 fastkeygen = 1;
945 else if (*arg == 's') {
946 #ifdef STATISTICS
947 reportdelay = 10000000;
948 #else
949 fprintf(stderr,"statistics support not compiled in\n");
950 exit(1);
951 #endif
953 else if (*arg == 'S') {
954 #ifdef STATISTICS
955 if (argc--)
956 reportdelay = (u64)atoll(*argv++) * 1000000;
957 else {
958 fprintf(stderr, "additional argument required\n");
959 exit(1);
961 #else
962 fprintf(stderr,"statistics support not compiled in\n");
963 exit(1);
964 #endif
966 else if (*arg == 'T') {
967 #ifdef STATISTICS
968 realtimestats = 0;
969 #else
970 fprintf(stderr,"statistics support not compiled in\n");
971 exit(1);
972 #endif
974 else {
975 fprintf(stderr, "unrecognised argument: -%c\n", *arg);
976 exit(1);
978 if (numargit)
979 goto nextarg;
981 else filters_add(arg);
984 if (!quietflag)
985 printfilters();
987 if (!filters_count())
988 return 0;
990 if (outfile)
991 fout = fopen(outfile, "w");
993 if (workdir)
994 mkdir(workdir, 0700);
996 direndpos = workdirlen;
997 onionendpos = workdirlen + ONIONLEN;
999 if (!dirnameflag) {
1000 printstartpos = direndpos;
1001 printlen = ONIONLEN + 1;
1002 } else {
1003 printstartpos = 0;
1004 printlen = onionendpos + 1;
1007 if (numthreads <= 0) {
1008 numthreads = cpucount();
1009 if (numthreads <= 0)
1010 numthreads = 1;
1013 signal(SIGTERM,termhandler);
1014 signal(SIGINT,termhandler);
1016 VEC_INIT(threads);
1017 VEC_ADDN(threads,numthreads);
1018 #ifdef STATISTICS
1019 VEC_INIT(stats);
1020 VEC_ADDN(stats,numthreads);
1021 VEC_ZERO(stats);
1022 VEC_INIT(tstats);
1023 VEC_ADDN(tstats,numthreads);
1024 VEC_ZERO(tstats);
1025 #endif
1027 for (size_t i = 0;i < VEC_LENGTH(threads);++i) {
1028 void *tp = 0;
1029 #ifdef STATISTICS
1030 tp = &VEC_BUF(stats,i);
1031 #endif
1032 tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp);
1033 if (tret) {
1034 fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
1035 exit(1);
1039 #ifdef STATISTICS
1040 struct timespec nowtime;
1041 u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
1042 if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
1043 fprintf(stderr, "failed to get time\n");
1044 exit(1);
1046 istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1047 #endif
1048 struct timespec ts;
1049 memset(&ts,0,sizeof(ts));
1050 ts.tv_nsec = 100000000;
1051 while (!endwork) {
1052 if (numneedgenerate && keysgenerated >= numneedgenerate) {
1053 endwork = 1;
1054 break;
1056 nanosleep(&ts,0);
1058 #ifdef STATISTICS
1059 clock_gettime(CLOCK_MONOTONIC,&nowtime);
1060 inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
1061 u64 sumcalc = 0,sumsuccess = 0;
1062 for (size_t i = 0;i < numthreads;++i) {
1063 u32 newt,tdiff;
1064 // numcalc
1065 newt = VEC_BUF(stats,i).numcalc.v;
1066 tdiff = newt - VEC_BUF(tstats,i).oldnumcalc;
1067 VEC_BUF(tstats,i).oldnumcalc = newt;
1068 VEC_BUF(tstats,i).numcalc += (u64)tdiff;
1069 sumcalc += VEC_BUF(tstats,i).numcalc;
1070 // numsuccess
1071 newt = VEC_BUF(stats,i).numsuccess.v;
1072 tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess;
1073 VEC_BUF(tstats,i).oldnumsuccess = newt;
1074 VEC_BUF(tstats,i).numsuccess += (u64)tdiff;
1075 sumsuccess += VEC_BUF(tstats,i).numsuccess;
1077 if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) {
1078 if (ireporttime)
1079 ireporttime += reportdelay;
1080 else
1081 ireporttime = inowtime;
1082 if (!ireporttime)
1083 ireporttime = 1;
1085 double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime);
1086 double successpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
1087 fprintf(stderr,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec,successpersec,(inowtime - istarttime + elapsedoffset) / 1000000.0);
1089 if (realtimestats) {
1090 for (size_t i = 0;i < numthreads;++i) {
1091 VEC_BUF(tstats,i).numcalc = 0;
1092 VEC_BUF(tstats,i).numsuccess = 0;
1094 elapsedoffset += inowtime - istarttime;
1095 istarttime = inowtime;
1098 if (sumcalc > U64_MAX / 2) {
1099 for (size_t i = 0;i < numthreads;++i) {
1100 VEC_BUF(tstats,i).numcalc /= 2;
1101 VEC_BUF(tstats,i).numsuccess /= 2;
1103 u64 timediff = (inowtime - istarttime + 1) / 2;
1104 elapsedoffset += timediff;
1105 istarttime += timediff;
1107 #endif
1110 if (!quietflag)
1111 fprintf(stderr, "waiting for threads to finish...\n");
1112 for (size_t i = 0;i < VEC_LENGTH(threads);++i)
1113 pthread_join(VEC_BUF(threads,i),0);
1114 if (!quietflag)
1115 fprintf(stderr, "done, quitting\n");
1117 pthread_mutex_destroy(&keysgenerated_mutex);
1118 pthread_mutex_destroy(&fout_mutex);
1119 filters_clean();
1121 if (outfile)
1122 fclose(fout);
1124 return 0;