10 #include <sodium/randombytes.h>
17 #include "ed25519/ed25519.h"
19 // additional leading zero is added by C
20 static const char * const pkprefix
= "== ed25519v1-public: type0 ==\0\0";
21 #define pkprefixlen (29 + 3)
22 static const char * const skprefix
= "== ed25519v1-secret: type0 ==\0\0";
23 #define skprefixlen (29 + 3)
24 static const char * const checksumstr
= ".onion checksum";
25 #define checksumstrlen 15
28 static char *workdir
= 0;
29 static size_t workdirlen
= 0;
31 static int quietflag
= 0;
36 // with checksum + version num
37 #define PUBONION_LEN (PUBLIC_LEN + 3)
38 // with newline included
41 static size_t onionendpos
; // end of .onion within string
42 static size_t direndpos
; // end of dir before .onion within string
43 static size_t printstartpos
; // where to start printing from
44 static size_t printlen
; // precalculated, related to printstartpos
46 static pthread_mutex_t fout_mutex
;
48 static size_t numneedgenerate
= 0;
49 static pthread_mutex_t keysgenerated_mutex
;
50 static volatile size_t keysgenerated
= 0;
51 static volatile int endwork
= 0;
53 static void termhandler(int sig
)
67 size_t len
; // real len minus one
75 VEC_STRUCT(ifiltervec
,struct intfilter
) ifilters
;
77 VEC_STRUCT(bfiltervec
,struct binfilter
) bfilters
;
80 static void filters_init()
89 static void filters_add(const char *filter
)
100 if (!base32_valid(filter
,&ret
)) {
101 fprintf(stderr
, "filter \"%s\" is invalid\n", filter
);
104 ret
= BASE32_FROM_LEN(ret
);
110 if (ret
> PUBLIC_LEN
)
113 fprintf(stderr
, "filter \"%s\" is too long\n", filter
);
116 ret2
= base32_from(bf
.f
,&bf
.mask
,filter
);
118 //printf("--m:%02X\n", bf.mask);
122 for (size_t i
= 0;i
< bf
.len
;++i
)
124 mc
.b
[bf
.len
] = bf
.mask
;
127 struct intfilter ifltr
= {fc
.i
,mc
.i
};
128 VEC_ADD(ifilters
,ifltr
);
130 VEC_ADD(bfilters
,bf
);
134 static void filters_clean()
143 static size_t filters_count()
146 return VEC_LENGTH(ifilters
);
148 return VEC_LENGTH(bfilters
);
154 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
155 #define MATCHFILTER(it,pk) ((*(u64 *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
159 #define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
160 #define MATCHFILTER(it,pk) ( \
161 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
162 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
166 static void loadfilterfile(const char *fname
)
169 FILE *f
= fopen(fname
,"r");
170 while (fgets(buf
,sizeof(buf
),f
)) {
171 for (char *p
= buf
;*p
;++p
) {
177 if (*buf
&& *buf
!= '#' && memcmp(buf
,"//",2) != 0)
182 static void printfilters()
186 l
= VEC_LENGTH(ifilters
);
188 l
= VEC_LENGTH(bfilters
);
191 fprintf(stderr
, "filters:\n");
193 fprintf(stderr
, "no filters defined\n");
195 for (i
= 0;i
< l
;++i
) {
196 char buf0
[256],buf1
[256];
200 u8
*imraw
= (u8
*)&VEC_BUF(ifilters
,i
).m
;
201 while (len
< 8 && imraw
[len
] != 0x00) ++len
;
202 u8 mask
= imraw
[len
-1];
203 u8
*ifraw
= (u8
*)&VEC_BUF(ifilters
,i
).f
;
205 size_t len
= VEC_BUF(bfilters
,i
).len
+ 1;
206 u8 mask
= VEC_BUF(bfilters
,i
).mask
;
207 u8
*ifraw
= VEC_BUF(bfilters
,i
).f
;
209 base32_to(buf0
,ifraw
,len
);
210 memcpy(bufx
,ifraw
,len
);
211 bufx
[len
- 1] |= ~mask
;
212 base32_to(buf1
,bufx
,len
);
213 char *a
= buf0
,*b
= buf1
;
214 while (*a
&& *a
== *b
)
217 fprintf(stderr
, "\t%s\n",buf0
);
221 // statistics, if enabled
226 size_t align_numcalc
;
230 size_t align_numsuccess
;
233 VEC_STRUCT(statsvec
,struct statstruct
);
241 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
245 static void onionready(char *sname
, const u8
*secret
, const u8
*pubonion
)
252 if (numneedgenerate
) {
253 pthread_mutex_lock(&keysgenerated_mutex
);
254 if (keysgenerated
>= numneedgenerate
) {
256 pthread_mutex_unlock(&keysgenerated_mutex
);
261 if (mkdir(sname
,0700) != 0) {
263 pthread_mutex_unlock(&keysgenerated_mutex
);
267 if (numneedgenerate
) {
269 if (keysgenerated
>= numneedgenerate
)
271 pthread_mutex_unlock(&keysgenerated_mutex
);
274 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
275 fh
= fopen(sname
, "wb");
277 fwrite(secret
, skprefixlen
+ SECRET_LEN
, 1, fh
);
281 strcpy(&sname
[onionendpos
], "/hostname");
282 fh
= fopen(sname
, "w");
284 sname
[onionendpos
] = '\n';
285 fwrite(&sname
[direndpos
], ONIONLEN
+1, 1, fh
);
289 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
290 fh
= fopen(sname
, "wb");
292 fwrite(pubonion
, pkprefixlen
+ PUBLIC_LEN
, 1, fh
);
297 sname
[onionendpos
] = '\n';
298 pthread_mutex_lock(&fout_mutex
);
299 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
301 pthread_mutex_unlock(&fout_mutex
);
306 static void addseed(u8
*seed
)
308 register unsigned int c
= 1;
309 for (size_t i
= 0;i
< SEED_LEN
;++i
) {
310 c
= (unsigned int)seed
[i
] + c
; seed
[i
] = c
& 0xFF; c
>>= 8;
316 static void *dowork(void *task
)
318 union pubonionunion
{
319 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
326 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
327 u8 secret
[skprefixlen
+ SECRET_LEN
];
328 u8
* const sk
= &secret
[skprefixlen
];
330 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
334 struct statstruct
*st
= (struct statstruct
*)task
;
337 memcpy(secret
,skprefix
,skprefixlen
);
338 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
339 // write version later as it will be overwritten by hash
340 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
341 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
343 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
345 memcpy(sname
,workdir
,workdirlen
);
348 randombytes(seed
,sizeof(seed
));
351 if (unlikely(endwork
))
354 ed25519_seckey_expand(sk
,seed
);
355 ed25519_pubkey(pk
,sk
);
362 if (unlikely(MATCHFILTER(i
,pk
))) {
367 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
368 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
370 pk
[PUBLIC_LEN
+ 2] = 0x03;
372 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
373 onionready(sname
, secret
, pubonion
.raw
);
385 static void addu64toscalar32(u8
*dst
,u64 v
)
389 for (i
= 0;i
< 32;++i
) {
390 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
396 static void *dofastwork(void *task
)
398 union pubonionunion
{
399 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
406 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
407 u8 secret
[skprefixlen
+ SECRET_LEN
];
408 u8
* const sk
= &secret
[skprefixlen
];
410 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
416 struct statstruct
*st
= (struct statstruct
*)task
;
419 memcpy(secret
, skprefix
, skprefixlen
);
420 memcpy(pubonion
.raw
, pkprefix
, pkprefixlen
);
421 // write version later as it will be overwritten by hash
422 memcpy(hashsrc
, checksumstr
, checksumstrlen
);
423 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
425 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
427 memcpy(sname
, workdir
, workdirlen
);
430 randombytes(seed
,sizeof(seed
));
431 ed25519_seckey_expand(sk
,seed
);
433 ge_scalarmult_base(&ge_public
,sk
);
434 ge_p3_tobytes(pk
,&ge_public
);
436 for (counter
= 0;counter
< U64_MAX
-8;counter
+= 8) {
439 if (unlikely(endwork
))
443 if (unlikely(MATCHFILTER(i
,pk
))) {
445 // update secret key with counter
446 addu64toscalar32(sk
,counter
);
448 if (((sk
[0] & 248) == sk
[0]) && (((sk
[31] & 63) | 64) == sk
[31])) {
449 /* These operations should be a no-op. */
459 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
460 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
462 pk
[PUBLIC_LEN
+ 2] = 0x03;
464 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
465 onionready(sname
,secret
,pubonion
.raw
);
466 // don't reuse same seed
472 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
473 ge_p1p1_to_p3(&ge_public
,&sum
);
474 ge_p3_tobytes(pk
,&ge_public
);
486 void printhelp(const char *progname
)
489 "Usage: %s filter [filter...] [options]\n"
490 " %s -f filterfile [options]\n"
492 "\t-h - print help\n"
493 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
494 "\t-q - do not print diagnostic output to stderr\n"
495 "\t-x - do not print onion names\n"
496 "\t-o filename - output onion names to specified file\n"
497 "\t-F - include directory names in onion names output\n"
498 "\t-d dirname - output directory\n"
499 "\t-t numthreads - specify number of threads (default - auto)\n"
500 "\t-j numthreads - same as -t\n"
501 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
502 "\t-z - use faster key generation method. this is now default\n"
503 "\t-Z - use slower key generation method\n"
504 "\t-s - print statistics each 10 seconds\n"
505 "\t-S t - print statistics every specified ammount of seconds\n"
506 "\t-T - do not reset statistics counters when printing\n"
511 void setworkdir(const char *wd
)
514 size_t l
= strlen(wd
);
519 fprintf(stderr
, "unset workdir\n");
525 char *s
= malloc(l
+ needslash
+ 1);
534 fprintf(stderr
,"set workdir: %s\n",workdir
);
537 VEC_STRUCT(threadvec
, pthread_t
);
539 int main(int argc
,char **argv
)
547 struct threadvec threads
;
549 struct statsvec stats
;
550 struct tstatsvec tstats
;
552 int realtimestats
= 1;
560 pthread_mutex_init(&keysgenerated_mutex
, 0);
561 pthread_mutex_init(&fout_mutex
, 0);
563 const char *progname
= argv
[0];
570 if (!ignoreargs
&& *arg
== '-') {
577 fprintf(stderr
, "unrecognised argument: -\n");
583 else if (!strcmp(arg
, "help"))
586 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
591 else if (*arg
== 0) {
596 else if (*arg
== 'h')
598 else if (*arg
== 'f') {
600 loadfilterfile(*argv
++);
602 fprintf(stderr
, "additional argument required\n");
606 else if (*arg
== 'q')
608 else if (*arg
== 'x')
610 else if (*arg
== 'o') {
614 fprintf(stderr
, "additional argument required\n");
618 else if (*arg
== 'F')
620 else if (*arg
== 'd') {
625 fprintf(stderr
, "additional argument required\n");
628 else if (*arg
== 't' || *arg
== 'j') {
630 numthreads
= atoi(*argv
++);
632 fprintf(stderr
, "additional argument required\n");
636 else if (*arg
== 'n') {
638 numneedgenerate
= (size_t)atoll(*argv
++);
640 fprintf(stderr
, "additional argument required\n");
644 else if (*arg
== 'Z')
646 else if (*arg
== 'z')
648 else if (*arg
== 's') {
650 reportdelay
= 10000000;
652 fprintf(stderr
,"statistics support not compiled in\n");
656 else if (*arg
== 'S') {
659 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
661 fprintf(stderr
, "additional argument required\n");
665 fprintf(stderr
,"statistics support not compiled in\n");
669 else if (*arg
== 'T') {
673 fprintf(stderr
,"statistics support not compiled in\n");
678 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
684 else filters_add(arg
);
690 if (!filters_count())
694 fout
= fopen(outfile
, "w");
697 mkdir(workdir
, 0700);
699 direndpos
= workdirlen
;
700 onionendpos
= workdirlen
+ ONIONLEN
;
703 printstartpos
= direndpos
;
704 printlen
= ONIONLEN
+ 1;
707 printlen
= onionendpos
+ 1;
710 if (numthreads
<= 0) {
715 signal(SIGTERM
,termhandler
);
716 signal(SIGINT
,termhandler
);
719 VEC_ADDN(threads
,pthread_t
,numthreads
);
723 VEC_ADDN(stats
,struct statstruct
,numthreads
);
724 VEC_ADDN(tstats
,struct tstatstruct
,numthreads
);
725 memset(&VEC_BUF(stats
,0),0,sizeof(struct statstruct
) * VEC_LENGTH(stats
));
726 memset(&VEC_BUF(tstats
,0),0,sizeof(struct tstatstruct
) * VEC_LENGTH(tstats
));
729 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
732 tp
= &VEC_BUF(stats
,i
);
734 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
736 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
742 struct timespec nowtime
;
743 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
744 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
745 fprintf(stderr
, "failed to get time\n");
748 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
751 memset(&ts
,0,sizeof(ts
));
752 ts
.tv_nsec
= 100000000;
754 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
761 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
762 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
763 u64 sumcalc
= 0,sumsuccess
= 0;
764 for (size_t i
= 0;i
< numthreads
;++i
) {
767 newt
= VEC_BUF(stats
,i
).numcalc
;
768 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
769 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
770 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
771 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
773 newt
= VEC_BUF(stats
,i
).numsuccess
;
774 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
775 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
776 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
777 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
779 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
781 ireporttime
+= reportdelay
;
783 ireporttime
= inowtime
;
787 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
788 double successpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
789 fprintf(stderr
,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec
,successpersec
,(inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
792 for (size_t i
= 0;i
< numthreads
;++i
) {
793 VEC_BUF(tstats
,i
).numcalc
= 0;
794 VEC_BUF(tstats
,i
).numsuccess
= 0;
796 elapsedoffset
+= inowtime
- istarttime
;
797 istarttime
= inowtime
;
800 if (sumcalc
> U64_MAX
/ 2) {
801 for (size_t i
= 0;i
< numthreads
;++i
) {
802 VEC_BUF(tstats
,i
).numcalc
/= 2;
803 VEC_BUF(tstats
,i
).numsuccess
/= 2;
805 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
806 elapsedoffset
+= timediff
;
807 istarttime
+= timediff
;
813 fprintf(stderr
, "waiting for threads to finish...\n");
814 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
815 pthread_join(VEC_BUF(threads
,i
),0);
817 fprintf(stderr
, "done, quitting\n");
819 pthread_mutex_destroy(&keysgenerated_mutex
);
820 pthread_mutex_destroy(&fout_mutex
);