1 #define _POSIX_C_SOURCE 200112L
11 #include <sodium/core.h>
12 #include <sodium/randombytes.h>
14 #include <sodium/crypto_pwhash.h>
16 #include <sodium/utils.h>
37 // Argon2 hashed passphrase stretching settings
38 // NOTE: changing these will break compatibility
39 #define PWHASH_OPSLIMIT 48
40 #define PWHASH_MEMLIMIT 64 * 1024 * 1024
41 #define PWHASH_ALG crypto_pwhash_ALG_ARGON2ID13
43 static int quietflag
= 0;
44 static int verboseflag
= 0;
46 static int wantdedup
= 0;
49 // 0, direndpos, onionendpos
50 // printstartpos = either 0 or direndpos
51 // printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
52 size_t onionendpos
; // end of .onion within string
53 size_t direndpos
; // end of dir before .onion within string
54 size_t printstartpos
; // where to start printing from
55 size_t printlen
; // precalculated, related to printstartpos
57 pthread_mutex_t fout_mutex
;
60 static void termhandler(int sig
)
79 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
82 static void printhelp(FILE *out
,const char *progname
)
85 "Usage: %s filter [filter...] [options]\n"
86 " %s -f filterfile [options]\n"
88 "\t-h - print help to stdout and quit\n"
89 "\t-f - specify filter file which contains filters separated by newlines\n"
90 "\t-D - deduplicate filters\n"
91 "\t-q - do not print diagnostic output to stderr\n"
92 "\t-x - do not print onion names\n"
93 "\t-v - print more diagnostic data\n"
94 "\t-o filename - output onion names to specified file (append)\n"
95 "\t-O filename - output onion names to specified file (overwrite)\n"
96 "\t-F - include directory names in onion names output\n"
97 "\t-d dirname - output directory\n"
98 "\t-t numthreads - specify number of threads to utilise (default - CPU core count or 1)\n"
99 "\t-j numthreads - same as -t\n"
100 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
101 "\t-N numwords - specify number of words per key (default - 1)\n"
102 "\t-z - use faster key generation method; this is now default\n"
103 "\t-Z - use slower key generation method\n"
104 "\t-B - use batching key generation method (>10x faster than -z, experimental)\n"
105 "\t-s - print statistics each 10 seconds\n"
106 "\t-S t - print statistics every specified ammount of seconds\n"
107 "\t-T - do not reset statistics counters when printing\n"
108 "\t-y - output generated keys in YAML format instead of dumping them to filesystem\n"
109 "\t-Y [filename [host.onion]] - parse YAML encoded input and extract key(s) to filesystem\n"
110 "\t--rawyaml - raw (unprefixed) public/secret keys for -y/-Y (may be useful for tor controller API)\n"
112 "\t-p passphrase - use passphrase to initialize the random seed with\n"
113 "\t-P - same as -p, but takes passphrase from PASSPHRASE environment variable\n"
119 static void e_additional(void)
121 fprintf(stderr
,"additional argument required\n");
126 static void e_nostatistics(void)
128 fprintf(stderr
,"statistics support not compiled in\n");
133 static void setworkdir(const char *wd
)
136 size_t l
= strlen(wd
);
141 fprintf(stderr
,"unset workdir\n");
144 unsigned needslash
= 0;
147 char *s
= (char *) malloc(l
+ needslash
+ 1);
158 fprintf(stderr
,"set workdir: %s\n",workdir
);
162 static void setpassphrase(const char *pass
)
164 static u8 salt
[crypto_pwhash_SALTBYTES
] = {0};
165 fprintf(stderr
,"expanding passphrase (may take a while)...");
166 if (crypto_pwhash(determseed
,sizeof(determseed
),
167 pass
,strlen(pass
),salt
,
168 PWHASH_OPSLIMIT
,PWHASH_MEMLIMIT
,PWHASH_ALG
) != 0)
170 fprintf(stderr
," out of memory!\n");
173 fprintf(stderr
," done.\n");
177 VEC_STRUCT(threadvec
, pthread_t
);
179 #include "filters_main.inc.h"
181 int main(int argc
,char **argv
)
183 const char *outfile
= 0;
184 const char *infile
= 0;
185 const char *onehostname
= 0;
194 int deterministic
= 0;
196 int outfileoverwrite
= 0;
197 struct threadvec threads
;
199 struct statsvec stats
;
200 struct tstatsvec tstats
;
202 int realtimestats
= 1;
206 if (sodium_init() < 0) {
207 fprintf(stderr
,"sodium_init() failed\n");
213 setvbuf(stderr
,0,_IONBF
,0);
216 const char *progname
= argv
[0];
218 printhelp(stderr
,progname
);
225 if (!ignoreargs
&& *arg
== '-') {
232 fprintf(stderr
,"unrecognised argument: -\n");
238 else if (!strcmp(arg
,"help") || !strcmp(arg
,"usage")) {
239 printhelp(stdout
,progname
);
242 else if (!strcmp(arg
,"rawyaml"))
245 fprintf(stderr
,"unrecognised argument: --%s\n",arg
);
250 else if (*arg
== 0) {
255 else if (*arg
== 'h') {
256 printhelp(stdout
,progname
);
259 else if (*arg
== 'f') {
261 const char *filename
= *argv
++;
262 if (!loadfilterfile(filename
)) {
263 fprintf(stderr
,"failed to load filter file %s\n",filename
);
270 else if (*arg
== 'D') {
274 fprintf(stderr
,"WARNING: deduplication isn't supported with regex filters\n");
277 else if (*arg
== 'q')
279 else if (*arg
== 'x')
281 else if (*arg
== 'v')
283 else if (*arg
== 'o') {
284 outfileoverwrite
= 0;
290 else if (*arg
== 'O') {
291 outfileoverwrite
= 1;
297 else if (*arg
== 'F')
299 else if (*arg
== 'd') {
305 else if (*arg
== 't' || *arg
== 'j') {
307 numthreads
= atoi(*argv
++);
311 else if (*arg
== 'n') {
313 numneedgenerate
= (size_t)atoll(*argv
++);
317 else if (*arg
== 'N') {
319 numwords
= atoi(*argv
++);
323 else if (*arg
== 'Z')
325 else if (*arg
== 'z')
327 else if (*arg
== 'B')
329 else if (*arg
== 's') {
331 reportdelay
= 10000000;
336 else if (*arg
== 'S') {
339 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
346 else if (*arg
== 'T') {
353 else if (*arg
== 'y')
355 else if (*arg
== 'Y') {
364 onehostname
= *argv
++;
367 if (onehostname
&& strlen(onehostname
) != ONION_LEN
) {
368 fprintf(stderr
,"bad onion argument length\n");
375 else if (*arg
== 'p') {
377 setpassphrase(*argv
++);
383 else if (*arg
== 'P') {
384 const char *pass
= getenv("PASSPHRASE");
386 fprintf(stderr
,"store passphrase in PASSPHRASE environment variable\n");
394 fprintf(stderr
,"unrecognised argument: -%c\n",*arg
);
404 if (yamlinput
&& yamloutput
) {
405 fprintf(stderr
,"both -y and -Y does not make sense\n");
409 if (yamlraw
&& !yamlinput
&& !yamloutput
) {
410 fprintf(stderr
,"--rawyaml requires either -y or -Y to do anything\n");
415 fout
= fopen(outfile
,!outfileoverwrite
? "a" : "w");
417 perror("failed to open output file");
422 if (!fout
&& yamloutput
) {
423 fprintf(stderr
,"nil output with yaml mode does not make sense\n");
428 createdir(workdir
,1);
430 direndpos
= workdirlen
;
431 onionendpos
= workdirlen
+ ONION_LEN
;
434 printstartpos
= direndpos
;
435 printlen
= ONION_LEN
+ 1; // + '\n'
438 printlen
= onionendpos
+ 1; // + '\n'
442 char *sname
= makesname();
445 fin
= fopen(infile
,"r");
447 fprintf(stderr
,"failed to open input file\n");
451 tret
= yamlin_parseandcreate(fin
,sname
,onehostname
,yamlraw
);
469 if (!filters_count() && !reportdelay
)
471 if (!filters_count())
476 if (numwords
> 1 && flattened
)
477 fprintf(stderr
,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
483 pthread_mutex_init(&keysgenerated_mutex
,0);
484 pthread_mutex_init(&fout_mutex
,0);
486 pthread_mutex_init(&determseed_mutex
,0);
489 if (numthreads
<= 0) {
490 numthreads
= cpucount();
495 fprintf(stderr
,"using %d %s\n",
496 numthreads
,numthreads
== 1 ? "thread" : "threads");
499 if (!quietflag
&& deterministic
&& numneedgenerate
!= 1)
500 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");
503 signal(SIGTERM
,termhandler
);
504 signal(SIGINT
,termhandler
);
507 VEC_ADDN(threads
,numthreads
);
510 VEC_ADDN(stats
,numthreads
);
513 VEC_ADDN(tstats
,numthreads
);
518 pthread_attr_t tattr
,*tattrp
= &tattr
;
519 tret
= pthread_attr_init(tattrp
);
521 perror("pthread_attr_init");
525 tret
= pthread_attr_setstacksize(tattrp
,80<<10);
527 perror("pthread_attr_setstacksize");
531 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
534 tp
= &VEC_BUF(stats
,i
);
536 tret
= pthread_create(&VEC_BUF(threads
,i
),0,
539 batchkeygen
? worker_batch_pass
: worker_fast_pass
) :
541 batchkeygen
? worker_batch
:
542 (fastkeygen
? worker_fast
: worker_slow
),tp
);
544 fprintf(stderr
,"error while making " FSZ
"th thread: %s\n",i
,strerror(tret
));
551 tret
= pthread_attr_destroy(tattrp
);
553 perror("pthread_attr_destroy");
558 struct timespec nowtime
;
559 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
560 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
561 perror("failed to get time");
564 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
567 memset(&ts
,0,sizeof(ts
));
568 ts
.tv_nsec
= 100000000;
570 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
577 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
578 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
579 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
580 for (int i
= 0;i
< numthreads
;++i
) {
583 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
584 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
585 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
586 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
587 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
589 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
590 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
591 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
592 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
593 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
595 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
596 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
597 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
598 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
599 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
601 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
603 ireporttime
+= reportdelay
;
605 ireporttime
= inowtime
;
609 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
610 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
611 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
612 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
613 calcpersec
,succpersec
,restpersec
,
614 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
617 for (int i
= 0;i
< numthreads
;++i
) {
618 VEC_BUF(tstats
,i
).numcalc
= 0;
619 VEC_BUF(tstats
,i
).numsuccess
= 0;
620 VEC_BUF(tstats
,i
).numrestart
= 0;
622 elapsedoffset
+= inowtime
- istarttime
;
623 istarttime
= inowtime
;
626 if (sumcalc
> U64_MAX
/ 2) {
627 for (int i
= 0;i
< numthreads
;++i
) {
628 VEC_BUF(tstats
,i
).numcalc
/= 2;
629 VEC_BUF(tstats
,i
).numsuccess
/= 2;
630 VEC_BUF(tstats
,i
).numrestart
/= 2;
632 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
633 elapsedoffset
+= timediff
;
634 istarttime
+= timediff
;
640 fprintf(stderr
,"waiting for threads to finish...");
641 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
642 pthread_join(VEC_BUF(threads
,i
),0);
644 fprintf(stderr
," done.\n");
650 pthread_mutex_destroy(&determseed_mutex
);
652 pthread_mutex_destroy(&fout_mutex
);
653 pthread_mutex_destroy(&keysgenerated_mutex
);