2 #define _POSIX_C_SOURCE 200112L
12 #include <sodium/randombytes.h>
13 #include <sodium/utils.h>
21 #include "ed25519/ed25519.h"
30 // additional 0 terminator is added by C
31 static const char * const pkprefix
= "== ed25519v1-public: type0 ==\0\0";
32 #define pkprefixlen (29 + 3)
33 static const char * const skprefix
= "== ed25519v1-secret: type0 ==\0\0";
34 #define skprefixlen (29 + 3)
35 static const char * const checksumstr
= ".onion checksum";
36 #define checksumstrlen 15
39 static char *workdir
= 0;
40 static size_t workdirlen
= 0;
42 static int quietflag
= 0;
43 //static int wantdedup = 0;
49 // with checksum + version num
50 #define PUBONION_LEN (PUBLIC_LEN + 3)
51 // with newline included
54 static size_t onionendpos
; // end of .onion within string
55 static size_t direndpos
; // end of dir before .onion within string
56 static size_t printstartpos
; // where to start printing from
57 static size_t printlen
; // precalculated, related to printstartpos
59 static pthread_mutex_t fout_mutex
;
61 static size_t numneedgenerate
= 0;
62 static int numwords
= 1;
63 static pthread_mutex_t keysgenerated_mutex
;
64 static volatile size_t keysgenerated
= 0;
65 static volatile int endwork
= 0;
67 static void termhandler(int sig
)
80 #define ADDNUMSUCCESS ++st->numsuccess.v
82 #define ADDNUMSUCCESS do ; while (0)
85 // statistics, if enabled
101 VEC_STRUCT(statsvec
,struct statstruct
);
111 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
115 static void onionready(char *sname
,const u8
*secret
,const u8
*pubonion
)
120 if (numneedgenerate
) {
121 pthread_mutex_lock(&keysgenerated_mutex
);
122 if (keysgenerated
>= numneedgenerate
) {
123 pthread_mutex_unlock(&keysgenerated_mutex
);
128 if (createdir(sname
,1) != 0) {
130 pthread_mutex_unlock(&keysgenerated_mutex
);
134 if (numneedgenerate
) {
136 if (keysgenerated
>= numneedgenerate
)
138 pthread_mutex_unlock(&keysgenerated_mutex
);
141 strcpy(&sname
[onionendpos
],"/hs_ed25519_secret_key");
142 writetofile(sname
,secret
,skprefixlen
+ SECRET_LEN
,1);
144 strcpy(&sname
[onionendpos
],"/hostname");
145 FILE *hfile
= fopen(sname
,"w");
147 sname
[onionendpos
] = '\n';
148 fwrite(&sname
[direndpos
],ONIONLEN
+ 1,1,hfile
);
152 strcpy(&sname
[onionendpos
],"/hs_ed25519_public_key");
153 writetofile(sname
,pubonion
,pkprefixlen
+ PUBLIC_LEN
,0);
156 sname
[onionendpos
] = '\n';
157 pthread_mutex_lock(&fout_mutex
);
158 fwrite(&sname
[printstartpos
],printlen
,1,fout
);
160 pthread_mutex_unlock(&fout_mutex
);
165 static void addsk32(u8
*sk
)
167 register unsigned int c
= 8;
168 for (size_t i
= 0;i
< 32;++i
) {
169 c
= (unsigned int)sk
[i
] + c
; sk
[i
] = c
& 0xFF; c
>>= 8;
175 // 0123 4567 xxxx --3--> 3456 7xxx
176 // 0123 4567 xxxx --1--> 1234 567x
177 static inline void shiftpk(u8
*dst
,const u8
*src
,size_t sbits
)
179 size_t i
,sbytes
= sbits
/ 8;
181 for (i
= 0;i
+ sbytes
< PUBLIC_LEN
;++i
) {
182 dst
[i
] = (u8
) ((src
[i
+sbytes
] << sbits
) |
183 (src
[i
+sbytes
+1] >> (8 - sbits
)));
185 for(;i
< PUBLIC_LEN
;++i
)
189 static void *dowork(void *task
)
191 union pubonionunion
{
192 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
199 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
200 u8 secret
[skprefixlen
+ SECRET_LEN
];
201 u8
* const sk
= &secret
[skprefixlen
];
203 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
204 u8 wpk
[PUBLIC_LEN
+ 1];
208 struct statstruct
*st
= (struct statstruct
*)task
;
212 memcpy(secret
,skprefix
,skprefixlen
);
214 memset(&pubonion
,0,sizeof(pubonion
));
215 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
216 // write version later as it will be overwritten by hash
217 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
218 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
220 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
224 memcpy(sname
,workdir
,workdirlen
);
227 randombytes(seed
,sizeof(seed
));
228 ed25519_seckey_expand(sk
,seed
);
234 if (unlikely(endwork
))
237 ed25519_pubkey(pk
,sk
);
245 shiftpk(wpk
,pk
,filter_len(i
));
248 DOFILTER(j
,wpk
,goto secondfind
);
253 shiftpk(wpk
,wpk
,filter_len(j
));
257 if ((sk
[0] & 248) != sk
[0] || ((sk
[31] & 63) | 64) != sk
[31])
263 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
264 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
266 pk
[PUBLIC_LEN
+ 2] = 0x03;
268 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
269 onionready(sname
,secret
,pubonion
.raw
);
280 sodium_memzero(secret
,sizeof(secret
));
281 sodium_memzero(seed
,sizeof(seed
));
285 static void addsztoscalar32(u8
*dst
,size_t v
)
289 for (i
= 0;i
< 32;++i
) {
290 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
296 static void *dofastwork(void *task
)
298 union pubonionunion
{
299 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
306 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
307 u8 secret
[skprefixlen
+ SECRET_LEN
];
308 u8
* const sk
= &secret
[skprefixlen
];
310 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
311 u8 wpk
[PUBLIC_LEN
+ 1];
317 struct statstruct
*st
= (struct statstruct
*)task
;
321 memcpy(secret
,skprefix
,skprefixlen
);
323 memset(&pubonion
,0,sizeof(pubonion
));
324 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
325 // write version later as it will be overwritten by hash
326 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
327 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
329 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
333 memcpy(sname
,workdir
,workdirlen
);
339 randombytes(seed
,sizeof(seed
));
340 ed25519_seckey_expand(sk
,seed
);
342 ge_scalarmult_base(&ge_public
,sk
);
343 ge_p3_tobytes(pk
,&ge_public
);
345 for (counter
= 0;counter
< SIZE_MAX
-8;counter
+= 8) {
348 if (unlikely(endwork
))
353 shiftpk(wpk
,pk
,filter_len(i
));
356 DOFILTER(j
,wpk
,goto secondfind
);
361 shiftpk(wpk
,wpk
,filter_len(j
));
365 // update secret key with counter
366 addsztoscalar32(sk
,counter
);
368 if ((sk
[0] & 248) != sk
[0] || ((sk
[31] & 63) | 64) != sk
[31])
374 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
375 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
377 pk
[PUBLIC_LEN
+ 2] = 0x03;
379 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
380 onionready(sname
,secret
,pubonion
.raw
);
382 // don't reuse same seed
386 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
387 ge_p1p1_to_p3(&ge_public
,&sum
);
388 ge_p3_tobytes(pk
,&ge_public
);
398 sodium_memzero(secret
,sizeof(secret
));
399 sodium_memzero(seed
,sizeof(seed
));
403 static void printhelp(FILE *out
,const char *progname
)
406 "Usage: %s filter [filter...] [options]\n"
407 " %s -f filterfile [options]\n"
409 "\t-h - print help to stdout and quit\n"
410 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
411 "\t-q - do not print diagnostic output to stderr\n"
412 "\t-x - do not print onion names\n"
413 "\t-o filename - output onion names to specified file\n"
414 "\t-F - include directory names in onion names output\n"
415 "\t-d dirname - output directory\n"
416 "\t-t numthreads - specify number of threads (default - auto)\n"
417 "\t-j numthreads - same as -t\n"
418 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
419 "\t-N numwords - specify number of words per key (default - 1)\n"
420 "\t-z - use faster key generation method. this is now default\n"
421 "\t-Z - use slower key generation method\n"
422 "\t-s - print statistics each 10 seconds\n"
423 "\t-S t - print statistics every specified ammount of seconds\n"
424 "\t-T - do not reset statistics counters when printing\n"
431 Q_UNRECOGNISED
= 101,
432 Q_NOSTATISTICS
= 102,
433 Q_FAILOPENOUTPUT
= 103,
438 static void e_additional()
440 fprintf(stderr
,"additional argument required\n");
445 static void e_nostatistics()
447 fprintf(stderr
,"statistics support not compiled in\n");
448 exit(Q_NOSTATISTICS
);
452 static void setworkdir(const char *wd
)
455 size_t l
= strlen(wd
);
460 fprintf(stderr
,"unset workdir\n");
463 unsigned needslash
= 0;
466 char *s
= malloc(l
+ needslash
+ 1);
477 fprintf(stderr
,"set workdir: %s\n",workdir
);
480 VEC_STRUCT(threadvec
, pthread_t
);
482 int main(int argc
,char **argv
)
490 struct threadvec threads
;
492 struct statsvec stats
;
493 struct tstatsvec tstats
;
495 int realtimestats
= 1;
502 setvbuf(stderr
,0,_IONBF
,0);
504 pthread_mutex_init(&keysgenerated_mutex
,0);
505 pthread_mutex_init(&fout_mutex
,0);
507 const char *progname
= argv
[0];
509 printhelp(stderr
,progname
);
516 if (!ignoreargs
&& *arg
== '-') {
523 fprintf(stderr
,"unrecognised argument: -\n");
524 exit(Q_UNRECOGNISED
);
529 else if (!strcmp(arg
,"help") || !strcmp(arg
,"usage")) {
530 printhelp(stdout
,progname
);
534 fprintf(stderr
,"unrecognised argument: --%s\n",arg
);
535 exit(Q_UNRECOGNISED
);
539 else if (*arg
== 0) {
544 else if (*arg
== 'h') {
545 printhelp(stdout
,progname
);
548 else if (*arg
== 'f') {
550 loadfilterfile(*argv
++);
554 else if (*arg
== 'q')
556 else if (*arg
== 'x')
558 else if (*arg
== 'o') {
564 else if (*arg
== 'F')
566 else if (*arg
== 'd') {
572 else if (*arg
== 't' || *arg
== 'j') {
574 numthreads
= atoi(*argv
++);
578 else if (*arg
== 'n') {
580 numneedgenerate
= (size_t)atoll(*argv
++);
584 else if (*arg
== 'N') {
586 numwords
= atoi(*argv
++);
590 else if (*arg
== 'Z')
592 else if (*arg
== 'z')
594 else if (*arg
== 's') {
596 reportdelay
= 10000000;
601 else if (*arg
== 'S') {
604 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
611 else if (*arg
== 'T') {
619 fprintf(stderr
,"unrecognised argument: -%c\n",*arg
);
620 exit(Q_UNRECOGNISED
);
625 else filters_add(arg
);
633 if (!filters_count() && !reportdelay
)
635 if (!filters_count())
640 if (numwords
> 1 && flattened
)
641 fprintf(stderr
,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
645 fout
= fopen(outfile
,"w");
647 perror("failed to open output file");
648 exit(Q_FAILOPENOUTPUT
);
653 createdir(workdir
,1);
655 direndpos
= workdirlen
;
656 onionendpos
= workdirlen
+ ONIONLEN
;
659 printstartpos
= direndpos
;
660 printlen
= ONIONLEN
+ 1;
663 printlen
= onionendpos
+ 1;
666 if (numthreads
<= 0) {
667 numthreads
= cpucount();
671 fprintf(stderr
,"using %d %s\n",
672 numthreads
,numthreads
== 1 ? "thread" : "threads");
675 signal(SIGTERM
,termhandler
);
676 signal(SIGINT
,termhandler
);
679 VEC_ADDN(threads
,numthreads
);
682 VEC_ADDN(stats
,numthreads
);
685 VEC_ADDN(tstats
,numthreads
);
689 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
692 tp
= &VEC_BUF(stats
,i
);
694 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
696 fprintf(stderr
,"error while making " FSZ
"th thread: %d (%s)\n",i
,tret
,strerror(tret
));
702 struct timespec nowtime
;
703 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
704 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
705 perror("failed to get time");
708 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
711 memset(&ts
,0,sizeof(ts
));
712 ts
.tv_nsec
= 100000000;
714 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
721 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
722 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + ((u64
)nowtime
.tv_nsec
/ 1000);
723 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
724 for (int i
= 0;i
< numthreads
;++i
) {
727 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
728 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
729 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
730 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
731 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
733 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
734 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
735 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
736 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
737 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
739 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
740 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
741 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
742 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
743 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
745 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
747 ireporttime
+= reportdelay
;
749 ireporttime
= inowtime
;
753 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
754 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
755 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
756 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
757 calcpersec
,succpersec
,restpersec
,
758 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
761 for (int i
= 0;i
< numthreads
;++i
) {
762 VEC_BUF(tstats
,i
).numcalc
= 0;
763 VEC_BUF(tstats
,i
).numsuccess
= 0;
764 VEC_BUF(tstats
,i
).numrestart
= 0;
766 elapsedoffset
+= inowtime
- istarttime
;
767 istarttime
= inowtime
;
770 if (sumcalc
> U64_MAX
/ 2) {
771 for (int i
= 0;i
< numthreads
;++i
) {
772 VEC_BUF(tstats
,i
).numcalc
/= 2;
773 VEC_BUF(tstats
,i
).numsuccess
/= 2;
774 VEC_BUF(tstats
,i
).numrestart
/= 2;
776 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
777 elapsedoffset
+= timediff
;
778 istarttime
+= timediff
;
784 fprintf(stderr
,"waiting for threads to finish...");
785 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
786 pthread_join(VEC_BUF(threads
,i
),0);
788 fprintf(stderr
," done.\n");
790 pthread_mutex_destroy(&keysgenerated_mutex
);
791 pthread_mutex_destroy(&fout_mutex
);