1 #define _POSIX_C_SOURCE 200112L
12 #include <sodium/core.h>
13 #include <sodium/randombytes.h>
15 #include <sodium/crypto_pwhash.h>
17 #include <sodium/utils.h>
38 // Argon2 hashed passphrase stretching settings
39 // NOTE: changing these will break compatibility
40 #define PWHASH_OPSLIMIT 48
41 #define PWHASH_MEMLIMIT 64 * 1024 * 1024
42 #define PWHASH_ALG crypto_pwhash_ALG_ARGON2ID13
44 static int quietflag
= 0;
45 static int verboseflag
= 0;
47 static int wantdedup
= 0;
50 // 0, direndpos, onionendpos
51 // printstartpos = either 0 or direndpos
52 // printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
53 size_t onionendpos
; // end of .onion within string
54 size_t direndpos
; // end of dir before .onion within string
55 size_t printstartpos
; // where to start printing from
56 size_t printlen
; // precalculated, related to printstartpos
58 pthread_mutex_t fout_mutex
;
61 static void termhandler(int sig
)
80 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
83 static void printhelp(FILE *out
,const char *progname
)
86 "Usage: %s filter [filter...] [options]\n"
87 " %s -f filterfile [options]\n"
89 "\t-h - print help to stdout and quit\n"
90 "\t-f - specify filter file which contains filters separated by newlines\n"
91 "\t-D - deduplicate filters\n"
92 "\t-q - do not print diagnostic output to stderr\n"
93 "\t-x - do not print onion names\n"
94 "\t-v - print more diagnostic data\n"
95 "\t-o filename - output onion names to specified file (append)\n"
96 "\t-O filename - output onion names to specified file (overwrite)\n"
97 "\t-F - include directory names in onion names output\n"
98 "\t-d dirname - output directory\n"
99 "\t-t numthreads - specify number of threads to utilise (default - CPU core count or 1)\n"
100 "\t-j numthreads - same as -t\n"
101 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
102 "\t-N numwords - specify number of words per key (default - 1)\n"
103 "\t-z - use faster key generation method; this is now default\n"
104 "\t-Z - use slower key generation method\n"
105 "\t-B - use batching key generation method (>10x faster than -z, experimental)\n"
106 "\t-s - print statistics each 10 seconds\n"
107 "\t-S t - print statistics every specified ammount of seconds\n"
108 "\t-T - do not reset statistics counters when printing\n"
109 "\t-y - output generated keys in YAML format instead of dumping them to filesystem\n"
110 "\t-Y [filename [host.onion]] - parse YAML encoded input and extract key(s) to filesystem\n"
111 "\t--rawyaml - raw (unprefixed) public/secret keys for -y/-Y (may be useful for tor controller API)\n"
113 "\t-p passphrase - use passphrase to initialize the random seed with\n"
114 "\t-P - same as -p, but takes passphrase from PASSPHRASE environment variable\n"
120 static void e_additional(void)
122 fprintf(stderr
,"additional argument required\n");
127 static void e_nostatistics(void)
129 fprintf(stderr
,"statistics support not compiled in\n");
134 static void setworkdir(const char *wd
)
137 size_t l
= strlen(wd
);
142 fprintf(stderr
,"unset workdir\n");
145 unsigned needslash
= 0;
148 char *s
= (char *) malloc(l
+ needslash
+ 1);
159 fprintf(stderr
,"set workdir: %s\n",workdir
);
163 static void setpassphrase(const char *pass
)
165 static u8 salt
[crypto_pwhash_SALTBYTES
] = {0};
166 fprintf(stderr
,"expanding passphrase (may take a while)...");
167 if (crypto_pwhash(determseed
,sizeof(determseed
),
168 pass
,strlen(pass
),salt
,
169 PWHASH_OPSLIMIT
,PWHASH_MEMLIMIT
,PWHASH_ALG
) != 0)
171 fprintf(stderr
," out of memory!\n");
174 fprintf(stderr
," done.\n");
178 VEC_STRUCT(threadvec
, pthread_t
);
180 #include "filters_main.inc.h"
182 int main(int argc
,char **argv
)
184 const char *outfile
= 0;
185 const char *infile
= 0;
186 const char *onehostname
= 0;
195 int deterministic
= 0;
197 int outfileoverwrite
= 0;
198 struct threadvec threads
;
200 struct statsvec stats
;
201 struct tstatsvec tstats
;
203 int realtimestats
= 1;
207 if (sodium_init() < 0) {
208 fprintf(stderr
,"sodium_init() failed\n");
214 setvbuf(stderr
,0,_IONBF
,0);
217 const char *progname
= argv
[0];
219 printhelp(stderr
,progname
);
226 if (!ignoreargs
&& *arg
== '-') {
233 fprintf(stderr
,"unrecognised argument: -\n");
239 else if (!strcmp(arg
,"help") || !strcmp(arg
,"usage")) {
240 printhelp(stdout
,progname
);
243 else if (!strcmp(arg
,"rawyaml"))
246 fprintf(stderr
,"unrecognised argument: --%s\n",arg
);
251 else if (*arg
== 0) {
256 else if (*arg
== 'h') {
257 printhelp(stdout
,progname
);
260 else if (*arg
== 'f') {
262 if (!loadfilterfile(*argv
++))
268 else if (*arg
== 'D') {
272 fprintf(stderr
,"WARNING: deduplication isn't supported with regex filters\n");
275 else if (*arg
== 'q')
277 else if (*arg
== 'x')
279 else if (*arg
== 'v')
281 else if (*arg
== 'o') {
282 outfileoverwrite
= 0;
288 else if (*arg
== 'O') {
289 outfileoverwrite
= 1;
295 else if (*arg
== 'F')
297 else if (*arg
== 'd') {
303 else if (*arg
== 't' || *arg
== 'j') {
305 numthreads
= atoi(*argv
++);
309 else if (*arg
== 'n') {
311 numneedgenerate
= (size_t)atoll(*argv
++);
315 else if (*arg
== 'N') {
317 numwords
= atoi(*argv
++);
321 else if (*arg
== 'Z')
323 else if (*arg
== 'z')
325 else if (*arg
== 'B')
327 else if (*arg
== 's') {
329 reportdelay
= 10000000;
334 else if (*arg
== 'S') {
337 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
344 else if (*arg
== 'T') {
351 else if (*arg
== 'y')
353 else if (*arg
== 'Y') {
362 onehostname
= *argv
++;
365 if (onehostname
&& strlen(onehostname
) != ONION_LEN
) {
366 fprintf(stderr
,"bad onion argument length\n");
373 else if (*arg
== 'p') {
375 setpassphrase(*argv
++);
381 else if (*arg
== 'P') {
382 const char *pass
= getenv("PASSPHRASE");
384 fprintf(stderr
,"store passphrase in PASSPHRASE environment variable\n");
392 fprintf(stderr
,"unrecognised argument: -%c\n",*arg
);
402 if (yamlinput
&& yamloutput
) {
403 fprintf(stderr
,"both -y and -Y does not make sense\n");
407 if (yamlraw
&& !yamlinput
&& !yamloutput
) {
408 fprintf(stderr
,"--rawyaml requires either -y or -Y to do anything\n");
413 fout
= fopen(outfile
,!outfileoverwrite
? "a" : "w");
415 perror("failed to open output file");
420 if (!fout
&& yamloutput
) {
421 fprintf(stderr
,"nil output with yaml mode does not make sense\n");
426 createdir(workdir
,1);
428 direndpos
= workdirlen
;
429 onionendpos
= workdirlen
+ ONION_LEN
;
432 printstartpos
= direndpos
;
433 printlen
= ONION_LEN
+ 1; // + '\n'
436 printlen
= onionendpos
+ 1; // + '\n'
440 char *sname
= makesname();
443 fin
= fopen(infile
,"r");
445 fprintf(stderr
,"failed to open input file\n");
449 tret
= yamlin_parseandcreate(fin
,sname
,onehostname
,yamlraw
);
467 if (!filters_count() && !reportdelay
)
469 if (!filters_count())
474 if (numwords
> 1 && flattened
)
475 fprintf(stderr
,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
481 pthread_mutex_init(&keysgenerated_mutex
,0);
482 pthread_mutex_init(&fout_mutex
,0);
484 pthread_mutex_init(&determseed_mutex
,0);
487 if (numthreads
<= 0) {
488 numthreads
= cpucount();
493 fprintf(stderr
,"using %d %s\n",
494 numthreads
,numthreads
== 1 ? "thread" : "threads");
497 if (!quietflag
&& deterministic
&& numneedgenerate
!= 1)
498 fprintf(stderr
,"CAUTION: avoid using keys generated with same password for unrelated services, as single leaked key may help attacker to regenerate related keys.\n");
501 signal(SIGTERM
,termhandler
);
502 signal(SIGINT
,termhandler
);
505 VEC_ADDN(threads
,numthreads
);
508 VEC_ADDN(stats
,numthreads
);
511 VEC_ADDN(tstats
,numthreads
);
516 pthread_attr_t tattr
,*tattrp
= &tattr
;
517 tret
= pthread_attr_init(tattrp
);
519 perror("pthread_attr_init");
523 tret
= pthread_attr_setstacksize(tattrp
,80<<10);
525 perror("pthread_attr_setstacksize");
529 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
532 tp
= &VEC_BUF(stats
,i
);
534 tret
= pthread_create(&VEC_BUF(threads
,i
),0,
537 batchkeygen
? worker_batch_pass
: worker_fast_pass
) :
539 batchkeygen
? worker_batch
:
540 (fastkeygen
? worker_fast
: worker_slow
),tp
);
542 fprintf(stderr
,"error while making " FSZ
"th thread: %s\n",i
,strerror(tret
));
549 tret
= pthread_attr_destroy(tattrp
);
551 perror("pthread_attr_destroy");
556 struct timespec nowtime
;
557 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
558 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
559 perror("failed to get time");
562 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
565 memset(&ts
,0,sizeof(ts
));
566 ts
.tv_nsec
= 100000000;
568 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
575 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
576 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
577 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
578 for (int i
= 0;i
< numthreads
;++i
) {
581 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
582 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
583 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
584 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
585 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
587 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
588 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
589 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
590 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
591 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
593 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
594 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
595 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
596 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
597 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
599 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
601 ireporttime
+= reportdelay
;
603 ireporttime
= inowtime
;
607 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
608 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
609 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
610 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
611 calcpersec
,succpersec
,restpersec
,
612 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
615 for (int i
= 0;i
< numthreads
;++i
) {
616 VEC_BUF(tstats
,i
).numcalc
= 0;
617 VEC_BUF(tstats
,i
).numsuccess
= 0;
618 VEC_BUF(tstats
,i
).numrestart
= 0;
620 elapsedoffset
+= inowtime
- istarttime
;
621 istarttime
= inowtime
;
624 if (sumcalc
> U64_MAX
/ 2) {
625 for (int i
= 0;i
< numthreads
;++i
) {
626 VEC_BUF(tstats
,i
).numcalc
/= 2;
627 VEC_BUF(tstats
,i
).numsuccess
/= 2;
628 VEC_BUF(tstats
,i
).numrestart
/= 2;
630 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
631 elapsedoffset
+= timediff
;
632 istarttime
+= timediff
;
638 fprintf(stderr
,"waiting for threads to finish...");
639 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
640 pthread_join(VEC_BUF(threads
,i
),0);
642 fprintf(stderr
," done.\n");
648 pthread_mutex_destroy(&determseed_mutex
);
650 pthread_mutex_destroy(&fout_mutex
);
651 pthread_mutex_destroy(&keysgenerated_mutex
);