2 #define _POSIX_C_SOURCE 200112L
13 #include <sodium/randombytes.h>
21 #include "ed25519/ed25519.h"
23 // additional leading zero 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
)
654 if (numneedgenerate
) {
655 pthread_mutex_lock(&keysgenerated_mutex
);
656 if (keysgenerated
>= numneedgenerate
) {
657 pthread_mutex_unlock(&keysgenerated_mutex
);
662 if (mkdir(sname
,0700) != 0) {
664 pthread_mutex_unlock(&keysgenerated_mutex
);
668 if (numneedgenerate
) {
670 if (keysgenerated
>= numneedgenerate
)
672 pthread_mutex_unlock(&keysgenerated_mutex
);
675 strcpy(&sname
[onionendpos
], "/hs_ed25519_secret_key");
676 fh
= fopen(sname
, "wb");
678 fwrite(secret
, skprefixlen
+ SECRET_LEN
, 1, fh
);
682 strcpy(&sname
[onionendpos
], "/hostname");
683 fh
= fopen(sname
, "w");
685 sname
[onionendpos
] = '\n';
686 fwrite(&sname
[direndpos
], ONIONLEN
+1, 1, fh
);
690 strcpy(&sname
[onionendpos
], "/hs_ed25519_public_key");
691 fh
= fopen(sname
, "wb");
693 fwrite(pubonion
, pkprefixlen
+ PUBLIC_LEN
, 1, fh
);
698 sname
[onionendpos
] = '\n';
699 pthread_mutex_lock(&fout_mutex
);
700 fwrite(&sname
[printstartpos
], printlen
, 1, fout
);
702 pthread_mutex_unlock(&fout_mutex
);
707 static void addseed(u8
*seed
)
709 register unsigned int c
= 1;
710 for (size_t i
= 0;i
< SEED_LEN
;++i
) {
711 c
= (unsigned int)seed
[i
] + c
; seed
[i
] = c
& 0xFF; c
>>= 8;
717 // 0123 4567 xxxx --3--> 3456 7xxx
718 // 0123 4567 xxxx --1--> 1234 567x
719 static inline void shiftpk(u8
*dst
,const u8
*src
,size_t sbits
)
721 size_t i
,sbytes
= sbits
/ 8;
723 for (i
= 0;i
+ sbytes
< PUBLIC_LEN
;++i
) {
724 dst
[i
] = (src
[i
+sbytes
] << sbits
) |
725 (src
[i
+sbytes
+1] >> (8 - sbits
));
727 for(;i
< PUBLIC_LEN
;++i
)
731 static void *dowork(void *task
)
733 union pubonionunion
{
734 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
741 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
742 u8 secret
[skprefixlen
+ SECRET_LEN
];
743 u8
* const sk
= &secret
[skprefixlen
];
745 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
746 u8 wpk
[PUBLIC_LEN
+ 1];
750 struct statstruct
*st
= (struct statstruct
*)task
;
753 memcpy(secret
,skprefix
,skprefixlen
);
755 memset(&pubonion
,0,sizeof(pubonion
));
756 memcpy(pubonion
.raw
,pkprefix
,pkprefixlen
);
757 // write version later as it will be overwritten by hash
758 memcpy(hashsrc
,checksumstr
,checksumstrlen
);
759 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
761 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
765 memcpy(sname
,workdir
,workdirlen
);
768 randombytes(seed
,sizeof(seed
));
774 if (unlikely(endwork
))
777 ed25519_seckey_expand(sk
,seed
);
778 ed25519_pubkey(pk
,sk
);
786 shiftpk(wpk
,pk
,filter_len(i
));
789 DOFILTER(j
,wpk
,goto secondfind
);
794 shiftpk(wpk
,wpk
,filter_len(j
));
801 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
802 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
804 pk
[PUBLIC_LEN
+ 2] = 0x03;
806 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
), ".onion");
807 onionready(sname
, secret
, pubonion
.raw
);
820 static void addsztoscalar32(u8
*dst
,size_t v
)
824 for (i
= 0;i
< 32;++i
) {
825 c
+= *dst
+ (v
& 0xFF); *dst
= c
& 0xFF; c
>>= 8;
831 static void *dofastwork(void *task
)
833 union pubonionunion
{
834 u8 raw
[pkprefixlen
+ PUBLIC_LEN
+ 32];
841 u8
* const pk
= &pubonion
.raw
[pkprefixlen
];
842 u8 secret
[skprefixlen
+ SECRET_LEN
];
843 u8
* const sk
= &secret
[skprefixlen
];
845 u8 hashsrc
[checksumstrlen
+ PUBLIC_LEN
+ 1];
846 u8 wpk
[PUBLIC_LEN
+ 1];
852 struct statstruct
*st
= (struct statstruct
*)task
;
855 memcpy(secret
, skprefix
, skprefixlen
);
857 memset(&pubonion
,0,sizeof(pubonion
));
858 memcpy(pubonion
.raw
, pkprefix
, pkprefixlen
);
859 // write version later as it will be overwritten by hash
860 memcpy(hashsrc
, checksumstr
, checksumstrlen
);
861 hashsrc
[checksumstrlen
+ PUBLIC_LEN
] = 0x03; // version
863 sname
= malloc(workdirlen
+ ONIONLEN
+ 63 + 1);
867 memcpy(sname
, workdir
, workdirlen
);
873 randombytes(seed
,sizeof(seed
));
874 ed25519_seckey_expand(sk
,seed
);
876 ge_scalarmult_base(&ge_public
,sk
);
877 ge_p3_tobytes(pk
,&ge_public
);
879 for (counter
= 0;counter
< SIZE_MAX
-8;counter
+= 8) {
882 if (unlikely(endwork
))
887 shiftpk(wpk
,pk
,filter_len(i
));
890 DOFILTER(j
,wpk
,goto secondfind
);
895 shiftpk(wpk
,wpk
,filter_len(j
));
899 // update secret key with counter
900 addsztoscalar32(sk
,counter
);
902 if (((sk
[0] & 248) == sk
[0]) && (((sk
[31] & 63) | 64) == sk
[31])) {
903 /* These operations should be a no-op. */
914 memcpy(&hashsrc
[checksumstrlen
],pk
,PUBLIC_LEN
);
915 FIPS202_SHA3_256(hashsrc
,sizeof(hashsrc
),&pk
[PUBLIC_LEN
]);
917 pk
[PUBLIC_LEN
+ 2] = 0x03;
919 strcpy(base32_to(&sname
[direndpos
],pk
,PUBONION_LEN
),".onion");
920 onionready(sname
,secret
,pubonion
.raw
);
922 // don't reuse same seed
926 ge_add(&sum
, &ge_public
,&ge_eightpoint
);
927 ge_p1p1_to_p3(&ge_public
,&sum
);
928 ge_p3_tobytes(pk
,&ge_public
);
940 void printhelp(const char *progname
)
943 "Usage: %s filter [filter...] [options]\n"
944 " %s -f filterfile [options]\n"
946 "\t-h - print help\n"
947 "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n"
948 "\t-q - do not print diagnostic output to stderr\n"
949 "\t-x - do not print onion names\n"
950 "\t-o filename - output onion names to specified file\n"
951 "\t-F - include directory names in onion names output\n"
952 "\t-d dirname - output directory\n"
953 "\t-t numthreads - specify number of threads (default - auto)\n"
954 "\t-j numthreads - same as -t\n"
955 "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
956 "\t-N numwords - specify number of words per key (default - 1)\n"
957 "\t-z - use faster key generation method. this is now default\n"
958 "\t-Z - use slower key generation method\n"
959 "\t-s - print statistics each 10 seconds\n"
960 "\t-S t - print statistics every specified ammount of seconds\n"
961 "\t-T - do not reset statistics counters when printing\n"
966 void setworkdir(const char *wd
)
969 size_t l
= strlen(wd
);
974 fprintf(stderr
, "unset workdir\n");
980 char *s
= malloc(l
+ needslash
+ 1);
991 fprintf(stderr
,"set workdir: %s\n",workdir
);
994 VEC_STRUCT(threadvec
, pthread_t
);
996 int main(int argc
,char **argv
)
1001 int dirnameflag
= 0;
1004 struct threadvec threads
;
1006 struct statsvec stats
;
1007 struct tstatsvec tstats
;
1008 u64 reportdelay
= 0;
1009 int realtimestats
= 1;
1013 ge_initeightpoint();
1017 pthread_mutex_init(&keysgenerated_mutex
, 0);
1018 pthread_mutex_init(&fout_mutex
, 0);
1020 const char *progname
= argv
[0];
1022 printhelp(progname
);
1027 if (!ignoreargs
&& *arg
== '-') {
1034 fprintf(stderr
, "unrecognised argument: -\n");
1040 else if (!strcmp(arg
, "help"))
1041 printhelp(progname
);
1043 fprintf(stderr
, "unrecognised argument: --%s\n", arg
);
1048 else if (*arg
== 0) {
1053 else if (*arg
== 'h')
1054 printhelp(progname
);
1055 else if (*arg
== 'f') {
1057 loadfilterfile(*argv
++);
1059 fprintf(stderr
, "additional argument required\n");
1063 else if (*arg
== 'q')
1065 else if (*arg
== 'x')
1067 else if (*arg
== 'o') {
1071 fprintf(stderr
, "additional argument required\n");
1075 else if (*arg
== 'F')
1077 else if (*arg
== 'd') {
1079 setworkdir(*argv
++);
1082 fprintf(stderr
, "additional argument required\n");
1085 else if (*arg
== 't' || *arg
== 'j') {
1087 numthreads
= atoi(*argv
++);
1089 fprintf(stderr
, "additional argument required\n");
1093 else if (*arg
== 'n') {
1095 numneedgenerate
= (size_t)atoll(*argv
++);
1097 fprintf(stderr
, "additional argument required\n");
1101 else if (*arg
== 'N') {
1103 numwords
= atoi(*argv
++);
1105 fprintf(stderr
, "additional argument required\n");
1109 else if (*arg
== 'Z')
1111 else if (*arg
== 'z')
1113 else if (*arg
== 's') {
1115 reportdelay
= 10000000;
1117 fprintf(stderr
,"statistics support not compiled in\n");
1121 else if (*arg
== 'S') {
1124 reportdelay
= (u64
)atoll(*argv
++) * 1000000;
1126 fprintf(stderr
, "additional argument required\n");
1130 fprintf(stderr
,"statistics support not compiled in\n");
1134 else if (*arg
== 'T') {
1138 fprintf(stderr
,"statistics support not compiled in\n");
1143 fprintf(stderr
, "unrecognised argument: -%c\n", *arg
);
1149 else filters_add(arg
);
1157 if (!filters_count() && !reportdelay
)
1159 if (!filters_count())
1164 fout
= fopen(outfile
, "w");
1167 mkdir(workdir
, 0700);
1169 direndpos
= workdirlen
;
1170 onionendpos
= workdirlen
+ ONIONLEN
;
1173 printstartpos
= direndpos
;
1174 printlen
= ONIONLEN
+ 1;
1177 printlen
= onionendpos
+ 1;
1180 if (numthreads
<= 0) {
1181 numthreads
= cpucount();
1182 if (numthreads
<= 0)
1186 signal(SIGTERM
,termhandler
);
1187 signal(SIGINT
,termhandler
);
1190 VEC_ADDN(threads
,numthreads
);
1193 VEC_ADDN(stats
,numthreads
);
1196 VEC_ADDN(tstats
,numthreads
);
1200 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
) {
1203 tp
= &VEC_BUF(stats
,i
);
1205 tret
= pthread_create(&VEC_BUF(threads
,i
),0,fastkeygen
? dofastwork
: dowork
,tp
);
1207 fprintf(stderr
,"error while making %dth thread: %d\n",(int)i
,tret
);
1213 struct timespec nowtime
;
1214 u64 istarttime
,inowtime
,ireporttime
= 0,elapsedoffset
= 0;
1215 if (clock_gettime(CLOCK_MONOTONIC
,&nowtime
) < 0) {
1216 fprintf(stderr
, "failed to get time\n");
1219 istarttime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1222 memset(&ts
,0,sizeof(ts
));
1223 ts
.tv_nsec
= 100000000;
1225 if (numneedgenerate
&& keysgenerated
>= numneedgenerate
) {
1232 clock_gettime(CLOCK_MONOTONIC
,&nowtime
);
1233 inowtime
= (1000000 * (u64
)nowtime
.tv_sec
) + (nowtime
.tv_nsec
/ 1000);
1234 u64 sumcalc
= 0,sumsuccess
= 0,sumrestart
= 0;
1235 for (size_t i
= 0;i
< numthreads
;++i
) {
1238 newt
= VEC_BUF(stats
,i
).numcalc
.v
;
1239 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumcalc
;
1240 VEC_BUF(tstats
,i
).oldnumcalc
= newt
;
1241 VEC_BUF(tstats
,i
).numcalc
+= (u64
)tdiff
;
1242 sumcalc
+= VEC_BUF(tstats
,i
).numcalc
;
1244 newt
= VEC_BUF(stats
,i
).numsuccess
.v
;
1245 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumsuccess
;
1246 VEC_BUF(tstats
,i
).oldnumsuccess
= newt
;
1247 VEC_BUF(tstats
,i
).numsuccess
+= (u64
)tdiff
;
1248 sumsuccess
+= VEC_BUF(tstats
,i
).numsuccess
;
1250 newt
= VEC_BUF(stats
,i
).numrestart
.v
;
1251 tdiff
= newt
- VEC_BUF(tstats
,i
).oldnumrestart
;
1252 VEC_BUF(tstats
,i
).oldnumrestart
= newt
;
1253 VEC_BUF(tstats
,i
).numrestart
+= (u64
)tdiff
;
1254 sumrestart
+= VEC_BUF(tstats
,i
).numrestart
;
1256 if (reportdelay
&& (!ireporttime
|| inowtime
- ireporttime
>= reportdelay
)) {
1258 ireporttime
+= reportdelay
;
1260 ireporttime
= inowtime
;
1264 double calcpersec
= (1000000.0 * sumcalc
) / (inowtime
- istarttime
);
1265 double succpersec
= (1000000.0 * sumsuccess
) / (inowtime
- istarttime
);
1266 double restpersec
= (1000000.0 * sumrestart
) / (inowtime
- istarttime
);
1267 fprintf(stderr
,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n",
1268 calcpersec
,succpersec
,restpersec
,
1269 (inowtime
- istarttime
+ elapsedoffset
) / 1000000.0);
1271 if (realtimestats
) {
1272 for (size_t i
= 0;i
< numthreads
;++i
) {
1273 VEC_BUF(tstats
,i
).numcalc
= 0;
1274 VEC_BUF(tstats
,i
).numsuccess
= 0;
1275 VEC_BUF(tstats
,i
).numrestart
= 0;
1277 elapsedoffset
+= inowtime
- istarttime
;
1278 istarttime
= inowtime
;
1281 if (sumcalc
> U64_MAX
/ 2) {
1282 for (size_t i
= 0;i
< numthreads
;++i
) {
1283 VEC_BUF(tstats
,i
).numcalc
/= 2;
1284 VEC_BUF(tstats
,i
).numsuccess
/= 2;
1285 VEC_BUF(tstats
,i
).numrestart
/= 2;
1287 u64 timediff
= (inowtime
- istarttime
+ 1) / 2;
1288 elapsedoffset
+= timediff
;
1289 istarttime
+= timediff
;
1295 fprintf(stderr
, "waiting for threads to finish...\n");
1296 for (size_t i
= 0;i
< VEC_LENGTH(threads
);++i
)
1297 pthread_join(VEC_BUF(threads
,i
),0);
1299 fprintf(stderr
, "done, quitting\n");
1301 pthread_mutex_destroy(&keysgenerated_mutex
);
1302 pthread_mutex_destroy(&fout_mutex
);