1 #define _POSIX_C_SOURCE 200112L
10 #include <sodium/core.h>
11 #include <sodium/randombytes.h>
13 #include <sodium/crypto_pwhash.h>
15 #include <sodium/utils.h>
36 // Argon2 hashed passphrase stretching settings
37 // NOTE: changing these will break compatibility
38 #define PWHASH_OPSLIMIT 48
39 #define PWHASH_MEMLIMIT 64 * 1024 * 1024
40 #define PWHASH_ALG crypto_pwhash_ALG_ARGON2ID13
42 static int quietflag
= 0;
43 static int verboseflag
= 0;
45 static int wantdedup
= 0;
48 // 0, direndpos, onionendpos
49 // printstartpos = either 0 or direndpos
50 // printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
51 size_t onionendpos
; // end of .onion within string
52 size_t direndpos
; // end of dir before .onion within string
53 size_t printstartpos
; // where to start printing from
54 size_t printlen
; // precalculated, related to printstartpos
56 pthread_mutex_t fout_mutex
;
59 static void termhandler(int sig
)
78 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
81 static void printhelp(FILE *out
,const char *progname
)
84 "Usage: %s filter [filter...] [options]\n"
85 " %s -f filterfile [options]\n"
87 "\t-h - print help to stdout and quit\n"
88 "\t-f - specify filter file which contains filters separated by newlines\n"
89 "\t-D - deduplicate filters\n"
90 "\t-q - do not print diagnostic output to stderr\n"
91 "\t-x - do not print onion names\n"
92 "\t-v - print more diagnostic data\n"
93 "\t-o filename - output onion names to specified file (append)\n"
94 "\t-O filename - output onion names to specified file (overwrite)\n"
95 "\t-F - include directory names in onion names output\n"
96 "\t-d dirname - output directory\n"
97 "\t-t numthreads - specify number of threads to utilise (default - CPU core count or 1)\n"
98 "\t-j numthreads - same as -t\n"
99 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
100 "\t-N numwords - specify number of words per key (default - 1)\n"
101 "\t-z - use faster key generation method; this is now default\n"
102 "\t-Z - use slower key generation method\n"
103 "\t-B - use batching key generation method (>10x faster than -z, experimental)\n"
104 "\t-s - print statistics each 10 seconds\n"
105 "\t-S t - print statistics every specified ammount of seconds\n"
106 "\t-T - do not reset statistics counters when printing\n"
107 "\t-y - output generated keys in YAML format instead of dumping them to filesystem\n"
108 "\t-Y [filename [host.onion]] - parse YAML encoded input and extract key(s) to filesystem\n"
110 "\t-p passphrase - use passphrase to initialize the random seed with\n"
111 "\t-P - same as -p, but takes passphrase from PASSPHRASE environment variable\n"
117 static void e_additional(void)
119 fprintf(stderr
,"additional argument required\n");
124 static void e_nostatistics(void)
126 fprintf(stderr
,"statistics support not compiled in\n");
131 static void setworkdir(const char *wd
)
134 size_t l
= strlen(wd
);
139 fprintf(stderr
,"unset workdir\n");
142 unsigned needslash
= 0;
145 char *s
= (char *) malloc(l
+ needslash
+ 1);
156 fprintf(stderr
,"set workdir: %s\n",workdir
);
160 static void setpassphrase(const char *pass
)
162 static u8 salt
[crypto_pwhash_SALTBYTES
] = {0};
163 fprintf(stderr
,"expanding passphrase (may take a while)...");
164 if (crypto_pwhash(determseed
,sizeof(determseed
),
165 pass
,strlen(pass
),salt
,
166 PWHASH_OPSLIMIT
,PWHASH_MEMLIMIT
,PWHASH_ALG
) != 0)
168 fprintf(stderr
," out of memory!\n");
171 fprintf(stderr
," done.\n");
175 VEC_STRUCT(threadvec
, pthread_t
);
177 #include "filters_main.inc.h"
179 int main(int argc
,char **argv
)
181 const char *outfile
= 0;
182 const char *infile
= 0;
183 const char *hostname
= 0;
192 int deterministic
= 0;
194 int outfileoverwrite
= 0;
195 struct threadvec threads
;
197 struct statsvec stats
;
198 struct tstatsvec tstats
;
200 int realtimestats
= 1;
204 if (sodium_init() < 0) {
205 fprintf(stderr
,"sodium_init() failed\n");
211 setvbuf(stderr
,0,_IONBF
,0);
214 const char *progname
= argv
[0];
216 printhelp(stderr
,progname
);
223 if (!ignoreargs
&& *arg
== '-') {
230 fprintf(stderr
,"unrecognised argument: -\n");
236 else if (!strcmp(arg
,"help") || !strcmp(arg
,"usage")) {
237 printhelp(stdout
,progname
);
241 fprintf(stderr
,"unrecognised argument: --%s\n",arg
);
246 else if (*arg
== 0) {
251 else if (*arg
== 'h') {
252 printhelp(stdout
,progname
);
255 else if (*arg
== 'f') {
257 loadfilterfile(*argv
++);
261 else if (*arg
== 'D') {
265 fprintf(stderr
,"WARNING: deduplication isn't supported with regex filters\n");
268 else if (*arg
== 'q')
270 else if (*arg
== 'x')
272 else if (*arg
== 'v')
274 else if (*arg
== 'o') {
275 outfileoverwrite
= 0;
281 else if (*arg
== 'O') {
282 outfileoverwrite
= 1;
288 else if (*arg
== 'F')
290 else if (*arg
== 'd') {
296 else if (*arg
== 't' || *arg
== 'j') {
298 numthreads
= atoi(*argv
++);
302 else if (*arg
== 'n') {
304 numneedgenerate
= (size_t)atoll(*argv
++);
308 else if (*arg
== 'N') {
310 numwords
= atoi(*argv
++);
314 else if (*arg
== 'Z')
316 else if (*arg
== 'z')
318 else if (*arg
== 'B')
320 else if (*arg
== 's') {
322 reportdelay
= 10000000;
327 else if (*arg
== 'S') {
330 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
337 else if (*arg
== 'T') {
344 else if (*arg
== 'y')
346 else if (*arg
== 'Y') {
358 if (hostname
&& strlen(hostname
) != ONION_LEN
) {
359 fprintf(stderr
,"bad onion argument length\n");
366 else if (*arg
== 'p') {
368 setpassphrase(*argv
++);
374 else if (*arg
== 'P') {
375 const char *pass
= getenv("PASSPHRASE");
377 fprintf(stderr
,"store passphrase in PASSPHRASE environment variable\n");
385 fprintf(stderr
,"unrecognised argument: -%c\n",*arg
);
396 fout
= fopen(outfile
,!outfileoverwrite
? "a" : "w");
398 perror("failed to open output file");
403 if (!fout
&& yamloutput
) {
404 fprintf(stderr
,"nil output with yaml mode does not make sense\n");
409 createdir(workdir
,1);
411 direndpos
= workdirlen
;
412 onionendpos
= workdirlen
+ ONION_LEN
;
415 printstartpos
= direndpos
;
416 printlen
= ONION_LEN
+ 1; // + '\n'
419 printlen
= onionendpos
+ 1; // + '\n'
423 char *sname
= makesname();
426 fin
= fopen(infile
,"r");
428 fprintf(stderr
,"failed to open input file\n");
432 tret
= yamlin_parseandcreate(fin
,sname
,hostname
);
450 if (!filters_count() && !reportdelay
)
452 if (!filters_count())
457 if (numwords
> 1 && flattened
)
458 fprintf(stderr
,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
464 pthread_mutex_init(&keysgenerated_mutex
,0);
465 pthread_mutex_init(&fout_mutex
,0);
467 pthread_mutex_init(&determseed_mutex
,0);
470 if (numthreads
<= 0) {
471 numthreads
= cpucount();
476 fprintf(stderr
,"using %d %s\n",
477 numthreads
,numthreads
== 1 ? "thread" : "threads");
480 if (!quietflag
&& deterministic
&& numneedgenerate
!= 1)
481 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");
484 signal(SIGTERM
,termhandler
);
485 signal(SIGINT
,termhandler
);
488 VEC_ADDN(threads
,numthreads
);
491 VEC_ADDN(stats
,numthreads
);
494 VEC_ADDN(tstats
,numthreads
);
499 pthread_attr_t tattr
,*tattrp
= &tattr
;
500 tret
= pthread_attr_init(tattrp
);
502 perror("pthread_attr_init");
506 tret
= pthread_attr_setstacksize(tattrp
,80<<10);
508 perror("pthread_attr_setstacksize");
512 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
515 tp
= &VEC_BUF(stats
,i
);
517 tret
= pthread_create(&VEC_BUF(threads
,i
),0,
520 batchkeygen
? worker_batch_pass
: worker_fast_pass
) :
522 batchkeygen
? worker_batch
:
523 (fastkeygen
? worker_fast
: worker_slow
),tp
);
525 fprintf(stderr
,"error while making " FSZ
"th thread: %s\n",i
,strerror(tret
));
532 tret
= pthread_attr_destroy(tattrp
);
534 perror("pthread_attr_destroy");
539 struct timespec nowtime
;
540 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
541 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
542 perror("failed to get time");
545 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
548 memset(&ts
,0,sizeof(ts
));
549 ts
.tv_nsec
= 100000000;
551 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
558 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
559 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
560 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
561 for (int i
= 0;i
< numthreads
;++i
) {
564 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
565 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
566 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
567 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
568 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
570 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
571 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
572 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
573 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
574 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
576 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
577 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
578 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
579 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
580 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
582 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
584 ireporttime
+= reportdelay
;
586 ireporttime
= inowtime
;
590 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
591 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
592 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
593 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
594 calcpersec
,succpersec
,restpersec
,
595 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
598 for (int i
= 0;i
< numthreads
;++i
) {
599 VEC_BUF(tstats
,i
).numcalc
= 0;
600 VEC_BUF(tstats
,i
).numsuccess
= 0;
601 VEC_BUF(tstats
,i
).numrestart
= 0;
603 elapsedoffset
+= inowtime
- istarttime
;
604 istarttime
= inowtime
;
607 if (sumcalc
> U64_MAX
/ 2) {
608 for (int i
= 0;i
< numthreads
;++i
) {
609 VEC_BUF(tstats
,i
).numcalc
/= 2;
610 VEC_BUF(tstats
,i
).numsuccess
/= 2;
611 VEC_BUF(tstats
,i
).numrestart
/= 2;
613 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
614 elapsedoffset
+= timediff
;
615 istarttime
+= timediff
;
621 fprintf(stderr
,"waiting for threads to finish...");
622 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
623 pthread_join(VEC_BUF(threads
,i
),0);
625 fprintf(stderr
," done.\n");
631 pthread_mutex_destroy(&determseed_mutex
);
633 pthread_mutex_destroy(&fout_mutex
);
634 pthread_mutex_destroy(&keysgenerated_mutex
);