2 #define _POSIX_C_SOURCE 200112L
12 #include <sodium/randombytes.h>
20 #include "ed25519/ed25519.h"
23 // additional 0 terminator is added by C
24 static const char * const pkprefix
= "== ed25519v1-public: type0 ==\0\0";
25 #define pkprefixlen (29 + 3)
26 static const char * const skprefix
= "== ed25519v1-secret: type0 ==\0\0";
27 #define skprefixlen (29 + 3)
28 static const char * const checksumstr
= ".onion checksum";
29 #define checksumstrlen 15
32 static char *workdir
= 0;
33 static size_t workdirlen
= 0;
35 static int quietflag
= 0;
40 // with checksum + version num
41 #define PUBONION_LEN (PUBLIC_LEN + 3)
42 // with newline included
45 static size_t onionendpos
; // end of .onion within string
46 static size_t direndpos
; // end of dir before .onion within string
47 static size_t printstartpos
; // where to start printing from
48 static size_t printlen
; // precalculated, related to printstartpos
50 static pthread_mutex_t fout_mutex
;
52 static size_t numneedgenerate
= 0;
53 static int numwords
= 1;
54 static pthread_mutex_t keysgenerated_mutex
;
55 static volatile size_t keysgenerated
= 0;
56 static volatile int endwork
= 0;
58 static void termhandler(int sig
)
83 #define BINFILTERLEN PUBLIC_LEN
87 size_t len
; // real len minus one
98 VEC_STRUCT(ifiltervec
,struct intfilter
) ifilters
;
103 VEC_STRUCT(bfiltervec
,struct binfilter
) bfilters
;
106 static void filters_init()
117 static void filter_sort(int (*compf
)(const void *,const void *))
119 qsort(&VEC_BUF(ifilters
,0),VEC_LENGTH(ifilters
),sizeof(struct intfilter
),compf
);
122 static inline size_t filter_len(size_t i
)
125 const u8
*m
= (const u8
*)&VEC_BUF(ifilters
,i
).m
;
127 const u8
*m
= (const u8
*)&ifiltermask
;
130 for (size_t j
= 0;;) {
132 for (size_t k
= 0;;) {
140 if (++j
>= sizeof(IFT
))
148 static int filter_compare(const void *p1
,const void *p2
)
150 if (((const struct intfilter
*)p1
)->f
< ((const struct intfilter
*)p2
)->f
)
152 if (((const struct intfilter
*)p1
)->f
> ((const struct intfilter
*)p2
)->f
)
160 * raw representation -- FF.FF.F0.00
161 * big endian -- 0xFFFFF000
162 * little endian -- 0x00F0FFFF
163 * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000
164 * 0x0000F000 + 1 -> 0x0000F001
165 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask
166 * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask
167 * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask
168 * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000
169 * 0x00f00000 + 1 -> 0x00f00001
170 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask
171 * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask
172 * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask
174 * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff
175 * 0x0FffFFff + 1 -> 0x10000000
176 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask
177 * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask
178 * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask
179 * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f
180 * 0xFFffFF0f + 1 -> 0xFFffFF10
181 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask
182 * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask
183 * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask
185 * essentially, we have to make direct mask + shifted mask bits worth of information
186 * and then split it into 2 parts
187 * we do not need absolute shifted mask shifting value, just relative to direct mask
188 * 0x0sss00dd - shifted & direct mask combo
189 * 0x000sssdd - combined mask
191 * generate values from 0x00000000 to 0x000sssdd
192 * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval)
194 * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000)
196 * above method doesn't work in some cases. better way:
197 * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000
198 * 0x800f0000 >> 16 -> 0x0000800f
199 * 0x0000800f + 1 -> 0x00008010
200 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask
201 * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask
204 #define EXPVAL(init,j,dmask,smask,ishift,rshift) \
205 ((init) | ((((j) & (dmask)) | (((j) << (rshift)) & (smask))) << (ishift)))
206 // add expanded set of values
207 // allocates space on its own
208 static void ifilter_addexpanded(
209 struct intfilter
*ifltr
,
210 IFT dmask
,IFT smask
,IFT cmask
,
211 int ishift
,int rshift
)
213 size_t i
= VEC_LENGTH(ifilters
);
214 VEC_ADDN(ifilters
,cmask
+ 1);
215 for (size_t j
= 0;;++j
) {
216 VEC_BUF(ifilters
,i
+ j
).f
= EXPVAL(ifltr
->f
,j
,dmask
,smask
,ishift
,rshift
);
222 // expand existing stuff
223 // allocates needed stuff on its own
224 static void ifilter_expand(IFT dmask
,IFT smask
,IFT cmask
,int ishift
,int rshift
)
226 size_t len
= VEC_LENGTH(ifilters
);
227 VEC_ADDN(ifilters
,cmask
* len
);
228 size_t esz
= cmask
+ 1; // size of expanded elements
229 for (size_t i
= len
- 1;;--i
) {
230 for (IFT j
= 0;;++j
) {
231 VEC_BUF(ifilters
,i
* esz
+ j
).f
= EXPVAL(VEC_BUF(ifilters
,i
).f
,j
,dmask
,smask
,ishift
,rshift
);
240 static inline void ifilter_addflatten(struct intfilter
*ifltr
,IFT mask
)
242 if (VEC_LENGTH(ifilters
) == 0) {
244 VEC_ADD(ifilters
,*ifltr
);
248 if (ifiltermask
== mask
) {
250 VEC_ADD(ifilters
,*ifltr
);
253 IFT cross
= ifiltermask
^ mask
;
255 while ((cross
& 1) == 0) {
259 IFT smask
= cross
& (cross
+ 1); // shift mask
260 IFT dmask
= cross
^ smask
; // direct mask
261 IFT cmask
; // combined mask
262 int rshift
= 0; // relative shift
263 while (cmask
= (smask
>> rshift
) | dmask
,(cmask
& (cmask
+ 1)) != 0)
266 if (ifiltermask
> mask
) {
267 // already existing stuff has more precise mask than we
268 // so we need to expand our stuff
269 ifilter_addexpanded(ifltr
,dmask
,smask
,cmask
,ishift
,rshift
);
273 ifilter_expand(dmask
,smask
,cmask
,ishift
,rshift
);
274 VEC_ADD(ifilters
,*ifltr
);
278 # endif // EXPANDMASK
283 * struct intfilter layout: filter,mask
284 * stuff is compared in big-endian way, so memcmp
285 * filter needs to be compared first
286 * if its equal, mask needs to be compared
287 * memcmp is aplicable there too
288 * due to struct intfilter layout, it all can be stuffed into one memcmp call
290 static int filter_compare(const void *p1
,const void *p2
)
292 return memcmp(p1
,p2
,sizeof(struct intfilter
));
298 static inline size_t filter_len(size_t i
)
300 size_t c
= VEC_BUF(bfilters
,i
).len
* 8;
301 u8 v
= VEC_BUF(bfilters
,i
).mask
;
302 for (size_t k
= 0;;) {
312 static void filter_sort(int (*compf
)(const void *,const void *))
314 qsort(&VEC_BUF(bfilters
,0),VEC_LENGTH(bfilters
),sizeof(struct binfilter
),compf
);
317 static int filter_compare(const void *p1
,const void *p2
)
319 const struct binfilter
*b1
= (const struct binfilter
*)p1
;
320 const struct binfilter
*b2
= (const struct binfilter
*)p2
;
321 size_t l
= b1
->len
<= b2
->len
? b1
->len
: b2
->len
;
322 int cmp
= memcmp(b1
->f
,b2
->f
,l
);
325 if (b1
->len
< b2
->len
)
327 if (b1
->len
> b2
->len
)
329 if (b1
->mask
< b2
->mask
)
331 if (b1
->mask
> b2
->mask
)
338 static void filters_add(const char *filter
)
349 // skip regex start symbol. we do not support regex tho
353 memset(&bf
,0,sizeof(bf
));
355 if (!base32_valid(filter
,&ret
)) {
356 fprintf(stderr
,"filter \"%s\" is invalid\n",filter
);
359 ret
= BASE32_FROM_LEN(ret
);
363 if (ret
> sizeof(IFT
))
365 if (ret
> sizeof(bf
.f
))
368 fprintf(stderr
,"filter \"%s\" is too long\n",filter
);
371 base32_from(bf
.f
,&bf
.mask
,filter
);
375 for (size_t i
= 0;i
< bf
.len
;++i
)
377 mc
.b
[bf
.len
] = bf
.mask
;
378 memcpy(fc
.b
,bf
.f
,sizeof(fc
.b
));
380 struct intfilter ifltr
= {
387 ifilter_addflatten(&ifltr
,mc
.i
);
389 VEC_ADD(ifilters
,ifltr
);
392 VEC_ADD(bfilters
,bf
);
396 static void filters_prepare()
399 fprintf(stderr
,"sorting filters...");
400 filter_sort(&filter_compare
);
402 fprintf(stderr
," done.\n");
403 // TODO remove duplicates
406 static void filters_clean()
415 static size_t filters_count()
418 return VEC_LENGTH(ifilters
);
420 return VEC_LENGTH(bfilters
);
425 #define ADDNUMSUCCESS ++st->numsuccess.v
427 #define ADDNUMSUCCESS do ; while (0)
434 #define MATCHFILTER(it,pk) \
435 ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
437 #define DOFILTER(it,pk,code) \
439 for (it = 0;it < VEC_LENGTH(ifilters);++it) { \
440 if (unlikely(MATCHFILTER(it,pk))) { \
451 #define DOFILTER(it,pk,code) \
453 register IFT maskedpk = *(IFT *)(pk) & ifiltermask; \
454 for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \
455 it = (up + down) / 2; \
456 if (maskedpk < VEC_BUF(ifilters,it).f) \
458 else if (maskedpk > VEC_BUF(ifilters,it).f) \
469 #define DOFILTER(it,pk,code) \
471 for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \
472 it = (up + down) / 2; \
473 IFT maskedpk = *(IFT *)(pk) & VEC_BUF(ifilters,it).m; \
474 register int cmp = memcmp(&maskedpk,&VEC_BUF(ifilters,it).f,sizeof(IFT)); \
494 #define MATCHFILTER(it,pk) ( \
495 memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \
496 (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len])
498 #define DOFILTER(it,pk,code) \
500 for (it = 0;it < VEC_LENGTH(bfilters);++it) { \
501 if (unlikely(MATCHFILTER(it,pk))) { \
510 #define DOFILTER(it,pk,code) \
512 for (size_t down = 0,up = VEC_LENGTH(bfilters);down < up;) { \
513 it = (up + down) / 2; \
515 register int filterdiff = memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len); \
516 if (filterdiff < 0) { \
520 if (filterdiff > 0) { \
525 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) < \
526 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
531 if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) > \
532 VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \
548 static void loadfilterfile(const char *fname
)
551 FILE *f
= fopen(fname
,"r");
552 while (fgets(buf
,sizeof(buf
),f
)) {
553 for (char *p
= buf
;*p
;++p
) {
559 if (*buf
&& *buf
!= '#' && memcmp(buf
,"//",2) != 0)
564 static void filters_print()
570 l
= VEC_LENGTH(ifilters
);
572 l
= VEC_LENGTH(bfilters
);
575 fprintf(stderr
, "filters:\n");
577 fprintf(stderr
, "no filters defined\n");
579 for (i
= 0;i
< l
;++i
) {
580 char buf0
[256],buf1
[256];
584 fprintf(stderr
,"[another %llu filters not shown]\n",(unsigned long long)(filters_count() - 20));
593 imraw
= (u8
*)&VEC_BUF(ifilters
,i
).m
;
595 imraw
= (u8
*)&ifiltermask
;
597 while (len
< sizeof(IFT
) && imraw
[len
] != 0x00) ++len
;
598 u8 mask
= imraw
[len
-1];
599 u8
*ifraw
= (u8
*)&VEC_BUF(ifilters
,i
).f
;
601 size_t len
= VEC_BUF(bfilters
,i
).len
+ 1;
602 u8 mask
= VEC_BUF(bfilters
,i
).mask
;
603 u8
*ifraw
= VEC_BUF(bfilters
,i
).f
;
605 base32_to(buf0
,ifraw
,len
);
606 memcpy(bufx
,ifraw
,len
);
607 bufx
[len
- 1] |= ~mask
;
608 base32_to(buf1
,bufx
,len
);
609 char *a
= buf0
,*b
= buf1
;
610 while (*a
&& *a
== *b
)
613 fprintf(stderr
, "\t%s\n",buf0
);
617 // statistics, if enabled
633 VEC_STRUCT(statsvec
,struct statstruct
);
643 VEC_STRUCT(tstatsvec
,struct tstatstruct
);
647 static void onionready(char *sname
, const u8
*secret
, const u8
*pubonion
)
652 if (numneedgenerate
) {
653 pthread_mutex_lock(&keysgenerated_mutex
);
654 if (keysgenerated
>= numneedgenerate
) {
655 pthread_mutex_unlock(&keysgenerated_mutex
);
660 if (createdir(sname
,1) != 0) {
662 pthread_mutex_unlock(&keysgenerated_mutex
);
666 if (numneedgenerate
) {
668 if (keysgenerated
>= numneedgenerate
)
670 pthread_mutex_unlock(&keysgenerated_mutex
);
673 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
674 writetofile(sname
,secret
,skprefixlen
+ SECRET_LEN
,1);
676 strcpy(&sname
[onionendpos
], "/hostname");
677 FILE *hfile
= fopen(sname
,"w");
679 sname
[onionendpos
] = '\n';
680 fwrite(&sname
[direndpos
],ONIONLEN
+ 1,1,hfile
);
684 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
685 writetofile(sname
,pubonion
,pkprefixlen
+ PUBLIC_LEN
,0);
688 sname
[onionendpos
] = '\n';
689 pthread_mutex_lock(&fout_mutex
);
690 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
692 pthread_mutex_unlock(&fout_mutex
);
697 static void addsk32(u8
*sk
)
699 register unsigned int c
= 8;
700 for (size_t i
= 0;i
< 32;++i
) {
701 c
= (unsigned int)sk
[i
] + c
; sk
[i
] = c
& 0xFF; c
>>= 8;
707 // 0123 4567 xxxx --3--> 3456 7xxx
708 // 0123 4567 xxxx --1--> 1234 567x
709 static inline void shiftpk(u8
*dst
,const u8
*src
,size_t sbits
)
711 size_t i
,sbytes
= sbits
/ 8;
713 for (i
= 0;i
+ sbytes
< PUBLIC_LEN
;++i
) {
714 dst
[i
] = (src
[i
+sbytes
] << sbits
) |
715 (src
[i
+sbytes
+1] >> (8 - sbits
));
717 for(;i
< PUBLIC_LEN
;++i
)
721 static void *dowork(void *task
)
723 union pubonionunion
{
724 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
731 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
732 u8 secret
[skprefixlen
+ SECRET_LEN
];
733 u8
* const sk
= &secret
[skprefixlen
];
735 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
736 u8 wpk
[PUBLIC_LEN
+ 1];
740 struct statstruct
*st
= (struct statstruct
*)task
;
743 memcpy(secret
,skprefix
,skprefixlen
);
745 memset(&pubonion
,0,sizeof(pubonion
));
746 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
747 // write version later as it will be overwritten by hash
748 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
749 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
751 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
755 memcpy(sname
,workdir
,workdirlen
);
758 randombytes(seed
,sizeof(seed
));
759 ed25519_seckey_expand(sk
,seed
);
765 if (unlikely(endwork
))
768 ed25519_pubkey(pk
,sk
);
776 shiftpk(wpk
,pk
,filter_len(i
));
779 DOFILTER(j
,wpk
,goto secondfind
);
784 shiftpk(wpk
,wpk
,filter_len(j
));
788 if ((sk
[0] & 248) != sk
[0] || ((sk
[31] & 63) | 64) != sk
[31])
794 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
795 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
797 pk
[PUBLIC_LEN
+ 2] = 0x03;
799 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
800 onionready(sname
,secret
,pubonion
.raw
);
813 static void addsztoscalar32(u8
*dst
,size_t v
)
817 for (i
= 0;i
< 32;++i
) {
818 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
824 static void *dofastwork(void *task
)
826 union pubonionunion
{
827 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
834 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
835 u8 secret
[skprefixlen
+ SECRET_LEN
];
836 u8
* const sk
= &secret
[skprefixlen
];
838 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
839 u8 wpk
[PUBLIC_LEN
+ 1];
845 struct statstruct
*st
= (struct statstruct
*)task
;
848 memcpy(secret
, skprefix
, skprefixlen
);
850 memset(&pubonion
,0,sizeof(pubonion
));
851 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
852 // write version later as it will be overwritten by hash
853 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
854 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
856 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
860 memcpy(sname
,workdir
,workdirlen
);
866 randombytes(seed
,sizeof(seed
));
867 ed25519_seckey_expand(sk
,seed
);
869 ge_scalarmult_base(&ge_public
,sk
);
870 ge_p3_tobytes(pk
,&ge_public
);
872 for (counter
= 0;counter
< SIZE_MAX
-8;counter
+= 8) {
875 if (unlikely(endwork
))
880 shiftpk(wpk
,pk
,filter_len(i
));
883 DOFILTER(j
,wpk
,goto secondfind
);
888 shiftpk(wpk
,wpk
,filter_len(j
));
892 // update secret key with counter
893 addsztoscalar32(sk
,counter
);
895 if ((sk
[0] & 248) != sk
[0] || ((sk
[31] & 63) | 64) != sk
[31])
901 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
902 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
904 pk
[PUBLIC_LEN
+ 2] = 0x03;
906 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
907 onionready(sname
,secret
,pubonion
.raw
);
909 // don't reuse same seed
913 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
914 ge_p1p1_to_p3(&ge_public
,&sum
);
915 ge_p3_tobytes(pk
,&ge_public
);
927 void printhelp(const char *progname
)
930 "Usage: %s filter [filter...] [options]\n"
931 " %s -f filterfile [options]\n"
933 "\t-h - print help\n"
934 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
935 "\t-q - do not print diagnostic output to stderr\n"
936 "\t-x - do not print onion names\n"
937 "\t-o filename - output onion names to specified file\n"
938 "\t-F - include directory names in onion names output\n"
939 "\t-d dirname - output directory\n"
940 "\t-t numthreads - specify number of threads (default - auto)\n"
941 "\t-j numthreads - same as -t\n"
942 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
943 "\t-N numwords - specify number of words per key (default - 1)\n"
944 "\t-z - use faster key generation method. this is now default\n"
945 "\t-Z - use slower key generation method\n"
946 "\t-s - print statistics each 10 seconds\n"
947 "\t-S t - print statistics every specified ammount of seconds\n"
948 "\t-T - do not reset statistics counters when printing\n"
953 void setworkdir(const char *wd
)
956 size_t l
= strlen(wd
);
961 fprintf(stderr
, "unset workdir\n");
967 char *s
= malloc(l
+ needslash
+ 1);
978 fprintf(stderr
,"set workdir: %s\n",workdir
);
981 VEC_STRUCT(threadvec
, pthread_t
);
983 int main(int argc
,char **argv
)
991 struct threadvec threads
;
993 struct statsvec stats
;
994 struct tstatsvec tstats
;
996 int realtimestats
= 1;
1000 ge_initeightpoint();
1004 pthread_mutex_init(&keysgenerated_mutex
, 0);
1005 pthread_mutex_init(&fout_mutex
, 0);
1007 const char *progname
= argv
[0];
1009 printhelp(progname
);
1014 if (!ignoreargs
&& *arg
== '-') {
1021 fprintf(stderr
, "unrecognised argument: -\n");
1027 else if (!strcmp(arg
, "help"))
1028 printhelp(progname
);
1030 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
1035 else if (*arg
== 0) {
1040 else if (*arg
== 'h')
1041 printhelp(progname
);
1042 else if (*arg
== 'f') {
1044 loadfilterfile(*argv
++);
1046 fprintf(stderr
, "additional argument required\n");
1050 else if (*arg
== 'q')
1052 else if (*arg
== 'x')
1054 else if (*arg
== 'o') {
1058 fprintf(stderr
, "additional argument required\n");
1062 else if (*arg
== 'F')
1064 else if (*arg
== 'd') {
1066 setworkdir(*argv
++);
1069 fprintf(stderr
, "additional argument required\n");
1072 else if (*arg
== 't' || *arg
== 'j') {
1074 numthreads
= atoi(*argv
++);
1076 fprintf(stderr
, "additional argument required\n");
1080 else if (*arg
== 'n') {
1082 numneedgenerate
= (size_t)atoll(*argv
++);
1084 fprintf(stderr
, "additional argument required\n");
1088 else if (*arg
== 'N') {
1090 numwords
= atoi(*argv
++);
1092 fprintf(stderr
, "additional argument required\n");
1096 else if (*arg
== 'Z')
1098 else if (*arg
== 'z')
1100 else if (*arg
== 's') {
1102 reportdelay
= 10000000;
1104 fprintf(stderr
,"statistics support not compiled in\n");
1108 else if (*arg
== 'S') {
1111 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
1113 fprintf(stderr
, "additional argument required\n");
1117 fprintf(stderr
,"statistics support not compiled in\n");
1121 else if (*arg
== 'T') {
1125 fprintf(stderr
,"statistics support not compiled in\n");
1130 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
1136 else filters_add(arg
);
1144 if (!filters_count() && !reportdelay
)
1146 if (!filters_count())
1151 fout
= fopen(outfile
, "w");
1154 createdir(workdir
,1);
1156 direndpos
= workdirlen
;
1157 onionendpos
= workdirlen
+ ONIONLEN
;
1160 printstartpos
= direndpos
;
1161 printlen
= ONIONLEN
+ 1;
1164 printlen
= onionendpos
+ 1;
1167 if (numthreads
<= 0) {
1168 numthreads
= cpucount();
1169 if (numthreads
<= 0)
1173 signal(SIGTERM
,termhandler
);
1174 signal(SIGINT
,termhandler
);
1177 VEC_ADDN(threads
,numthreads
);
1180 VEC_ADDN(stats
,numthreads
);
1183 VEC_ADDN(tstats
,numthreads
);
1187 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
1190 tp
= &VEC_BUF(stats
,i
);
1192 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
1194 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
1200 struct timespec nowtime
;
1201 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
1202 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
1203 fprintf(stderr
, "failed to get time\n");
1206 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1209 memset(&ts
,0,sizeof(ts
));
1210 ts
.tv_nsec
= 100000000;
1212 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
1219 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
1220 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1221 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
1222 for (size_t i
= 0;i
< numthreads
;++i
) {
1225 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
1226 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
1227 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
1228 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
1229 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
1231 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
1232 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
1233 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
1234 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
1235 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
1237 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
1238 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
1239 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
1240 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
1241 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
1243 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
1245 ireporttime
+= reportdelay
;
1247 ireporttime
= inowtime
;
1251 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
1252 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
1253 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
1254 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1255 calcpersec
,succpersec
,restpersec
,
1256 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
1258 if (realtimestats
) {
1259 for (size_t i
= 0;i
< numthreads
;++i
) {
1260 VEC_BUF(tstats
,i
).numcalc
= 0;
1261 VEC_BUF(tstats
,i
).numsuccess
= 0;
1262 VEC_BUF(tstats
,i
).numrestart
= 0;
1264 elapsedoffset
+= inowtime
- istarttime
;
1265 istarttime
= inowtime
;
1268 if (sumcalc
> U64_MAX
/ 2) {
1269 for (size_t i
= 0;i
< numthreads
;++i
) {
1270 VEC_BUF(tstats
,i
).numcalc
/= 2;
1271 VEC_BUF(tstats
,i
).numsuccess
/= 2;
1272 VEC_BUF(tstats
,i
).numrestart
/= 2;
1274 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
1275 elapsedoffset
+= timediff
;
1276 istarttime
+= timediff
;
1282 fprintf(stderr
, "waiting for threads to finish...\n");
1283 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
1284 pthread_join(VEC_BUF(threads
,i
),0);
1286 fprintf(stderr
, "done, quitting\n");
1288 pthread_mutex_destroy(&keysgenerated_mutex
);
1289 pthread_mutex_destroy(&fout_mutex
);