#ifdef __linux__ #define _POSIX_C_SOURCE 200112L #endif #include #include #include #include #include #include #include #include #include "types.h" #include "likely.h" #include "vec.h" #include "base32.h" #include "cpucount.h" #include "keccak.h" #include "ed25519/ed25519.h" #include "ioutil.h" // additional 0 terminator is added by C static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0"; #define pkprefixlen (29 + 3) static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0"; #define skprefixlen (29 + 3) static const char * const checksumstr = ".onion checksum"; #define checksumstrlen 15 // output directory static char *workdir = 0; static size_t workdirlen = 0; static int quietflag = 0; #define SECRET_LEN 64 #define PUBLIC_LEN 32 #define SEED_LEN 32 // with checksum + version num #define PUBONION_LEN (PUBLIC_LEN + 3) // with newline included #define ONIONLEN 62 static size_t onionendpos; // end of .onion within string static size_t direndpos; // end of dir before .onion within string static size_t printstartpos; // where to start printing from static size_t printlen; // precalculated, related to printstartpos static pthread_mutex_t fout_mutex; static FILE *fout; static size_t numneedgenerate = 0; static int numwords = 1; static pthread_mutex_t keysgenerated_mutex; static volatile size_t keysgenerated = 0; static volatile int endwork = 0; static void termhandler(int sig) { switch (sig) { case SIGTERM: case SIGINT: endwork = 1; break; } } // filters stuff #ifdef INTFILTER #ifdef BINSEARCH #ifndef BESORT #define OMITMASK #endif #endif #endif #ifdef OMITMASK #define EXPANDMASK #endif #ifndef BINFILTERLEN #define BINFILTERLEN PUBLIC_LEN #endif struct binfilter { u8 f[BINFILTERLEN]; size_t len; // real len minus one u8 mask; } ; #ifdef INTFILTER struct intfilter { IFT f; # ifndef OMITMASK IFT m; # endif } ; VEC_STRUCT(ifiltervec,struct intfilter) ifilters; # ifdef OMITMASK IFT ifiltermask; # endif // BINSEARCH #else // INTFILTER VEC_STRUCT(bfiltervec,struct binfilter) bfilters; #endif // INTFILTER static void filters_init() { #ifdef INTFILTER VEC_INIT(ifilters); #else VEC_INIT(bfilters); #endif } #ifdef INTFILTER static void filter_sort(int (*compf)(const void *,const void *)) { qsort(&VEC_BUF(ifilters,0),VEC_LENGTH(ifilters),sizeof(struct intfilter),compf); } static inline size_t filter_len(size_t i) { # ifndef OMITMASK const u8 *m = (const u8 *)&VEC_BUF(ifilters,i).m; # else // OMITMASK const u8 *m = (const u8 *)&ifiltermask; # endif // OMITMASK size_t c = 0; for (size_t j = 0;;) { u8 v = m[j]; for (size_t k = 0;;) { if (!v) return c; ++c; if (++k >= 8) break; v <<= 1; } if (++j >= sizeof(IFT)) break; } return c; } # ifdef OMITMASK static int filter_compare(const void *p1,const void *p2) { if (((const struct intfilter *)p1)->f < ((const struct intfilter *)p2)->f) return -1; if (((const struct intfilter *)p1)->f > ((const struct intfilter *)p2)->f) return 1; return 0; } # ifdef EXPANDMASK /* * raw representation -- FF.FF.F0.00 * big endian -- 0xFFFFF000 * little endian -- 0x00F0FFFF * b: 0xFFffF000 ^ 0xFFff0000 -> 0x0000F000 * 0x0000F000 + 1 -> 0x0000F001 * 0x0000F000 & 0x0000F001 -> 0x0000F000 <- shifted mask * 0x0000F000 ^ 0x0000F000 -> 0x00000000 <- direct mask * 0x0000F000 ^ 0x00000000 -> 0x0000F000 <- shifted mask * l: 0x00f0FFff ^ 0x0000FFff -> 0x00f00000 * 0x00f00000 + 1 -> 0x00f00001 * 0x00f00000 & 0x00f00001 -> 0x00f00000 <- shifted mask * 0x00f00000 ^ 0x00f00000 -> 0x00000000 <- direct mask * 0x00f00000 ^ 0x00000000 -> 0x00f00000 <- shifted mask * * b: 0xFFffFFff ^ 0xF0000000 -> 0x0FffFFff * 0x0FffFFff + 1 -> 0x10000000 * 0x0FffFFff & 0x10000000 -> 0x00000000 <- shifted mask * 0x0FffFFff ^ 0x00000000 -> 0x0FffFFff <- direct mask * 0x0FffFFff ^ 0x0FffFFff -> 0x00000000 <- shifted mask * l: 0xFFffFFff ^ 0x000000f0 -> 0xFFffFF0f * 0xFFffFF0f + 1 -> 0xFFffFF10 * 0xFFffFF0f & 0xFFffFF10 -> 0xFFffFF00 <- shifted mask * 0xFFffFF0f ^ 0xFFffFF00 -> 0x0000000f <- direct mask * 0xFFffFF0f ^ 0x0000000f -> 0xFFffFF00 <- shifted mask * * essentially, we have to make direct mask + shifted mask bits worth of information * and then split it into 2 parts * we do not need absolute shifted mask shifting value, just relative to direct mask * 0x0sss00dd - shifted & direct mask combo * 0x000sssdd - combined mask * 8 - relshiftval * generate values from 0x00000000 to 0x000sssdd * for each value, realmask <- (val & 0x000000dd) | ((val & 0x000sss00) << relshiftval) * or.. * realmask <- (val & 0x000000dd) | ((val << relshiftval) & 0x0sss0000) * ... * above method doesn't work in some cases. better way: * l: 0x80ffFFff ^ 0x00f0FFff -> 0x800f0000 * 0x800f0000 >> 16 -> 0x0000800f * 0x0000800f + 1 -> 0x00008010 * 0x0000800f & 0x00008010 -> 0x00008000 <- smask * 0x0000800f ^ 0x00008000 -> 0x0000000f <- dmask */ #define EXPVAL(init,j,dmask,smask,ishift,rshift) \ ((init) | ((((j) & (dmask)) | (((j) << (rshift)) & (smask))) << (ishift))) // add expanded set of values // allocates space on its own static void ifilter_addexpanded( struct intfilter *ifltr, IFT dmask,IFT smask,IFT cmask, int ishift,int rshift) { size_t i = VEC_LENGTH(ifilters); VEC_ADDN(ifilters,cmask + 1); for (size_t j = 0;;++j) { VEC_BUF(ifilters,i + j).f = EXPVAL(ifltr->f,j,dmask,smask,ishift,rshift); if (j == cmask) break; } } // expand existing stuff // allocates needed stuff on its own static void ifilter_expand(IFT dmask,IFT smask,IFT cmask,int ishift,int rshift) { size_t len = VEC_LENGTH(ifilters); VEC_ADDN(ifilters,cmask * len); size_t esz = cmask + 1; // size of expanded elements for (size_t i = len - 1;;--i) { for (IFT j = 0;;++j) { VEC_BUF(ifilters,i * esz + j).f = EXPVAL(VEC_BUF(ifilters,i).f,j,dmask,smask,ishift,rshift); if (j == cmask) break; } if (i == 0) break; } } static inline void ifilter_addflatten(struct intfilter *ifltr,IFT mask) { if (VEC_LENGTH(ifilters) == 0) { // simple VEC_ADD(ifilters,*ifltr); ifiltermask = mask; return; } if (ifiltermask == mask) { // lucky VEC_ADD(ifilters,*ifltr); return; } IFT cross = ifiltermask ^ mask; int ishift = 0; while ((cross & 1) == 0) { ++ishift; cross >>= 1; } IFT smask = cross & (cross + 1); // shift mask IFT dmask = cross ^ smask; // direct mask IFT cmask; // combined mask int rshift = 0; // relative shift while (cmask = (smask >> rshift) | dmask,(cmask & (cmask + 1)) != 0) ++rshift; // preparations done if (ifiltermask > mask) { // already existing stuff has more precise mask than we // so we need to expand our stuff ifilter_addexpanded(ifltr,dmask,smask,cmask,ishift,rshift); } else { ifiltermask = mask; ifilter_expand(dmask,smask,cmask,ishift,rshift); VEC_ADD(ifilters,*ifltr); } } # endif // EXPANDMASK # else // OMITMASK /* * struct intfilter layout: filter,mask * stuff is compared in big-endian way, so memcmp * filter needs to be compared first * if its equal, mask needs to be compared * memcmp is aplicable there too * due to struct intfilter layout, it all can be stuffed into one memcmp call */ static int filter_compare(const void *p1,const void *p2) { return memcmp(p1,p2,sizeof(struct intfilter)); } # endif // OMITMASK #else // INTFILTER static inline size_t filter_len(size_t i) { size_t c = VEC_BUF(bfilters,i).len * 8; u8 v = VEC_BUF(bfilters,i).mask; for (size_t k = 0;;) { if (!v) return c; ++c; if (++k >= 8) return c; v <<= 1; } } static void filter_sort(int (*compf)(const void *,const void *)) { qsort(&VEC_BUF(bfilters,0),VEC_LENGTH(bfilters),sizeof(struct binfilter),compf); } static int filter_compare(const void *p1,const void *p2) { const struct binfilter *b1 = (const struct binfilter *)p1; const struct binfilter *b2 = (const struct binfilter *)p2; size_t l = b1->len <= b2->len ? b1->len : b2->len; int cmp = memcmp(b1->f,b2->f,l); if (cmp) return cmp; if (b1->len < b2->len) return -1; if (b1->len > b2->len) return 1; if (b1->mask < b2->mask) return -1; if (b1->mask > b2->mask) return 1; return 0; } #endif // INTFILTER static void filters_add(const char *filter) { struct binfilter bf; size_t ret; #ifdef INTFILTER union intconv { IFT i; u8 b[sizeof(IFT)]; } fc,mc; #endif // skip regex start symbol. we do not support regex tho if (*filter == '^') ++filter; memset(&bf,0,sizeof(bf)); if (!base32_valid(filter,&ret)) { fprintf(stderr,"filter \"%s\" is invalid\n",filter); fprintf(stderr," "); while (ret--) fputc(' ',stderr); fprintf(stderr,"^\n"); return; } ret = BASE32_FROM_LEN(ret); if (!ret) return; #ifdef INTFILTER size_t maxsz = sizeof(IFT); #else size_t maxsz = sizeof(bf.f); #endif if (ret > maxsz) { fprintf(stderr,"filter \"%s\" is too long\n",filter); fprintf(stderr," "); maxsz = (maxsz * 8) / 5; while (maxsz--) fputc(' ',stderr); fprintf(stderr,"^\n"); return; } base32_from(bf.f,&bf.mask,filter); bf.len = ret - 1; #ifdef INTFILTER mc.i = 0; for (size_t i = 0;i < bf.len;++i) mc.b[i] = 0xFF; mc.b[bf.len] = bf.mask; memcpy(fc.b,bf.f,sizeof(fc.b)); fc.i &= mc.i; struct intfilter ifltr = { .f = fc.i, # ifndef OMITMASK .m = mc.i, # endif }; # ifdef OMITMASK ifilter_addflatten(&ifltr,mc.i); # else // OMITMASK VEC_ADD(ifilters,ifltr); # endif // OMITMASK #else // INTFILTER VEC_ADD(bfilters,bf); #endif // INTFILTER } static void filters_prepare() { if (!quietflag) fprintf(stderr,"sorting filters..."); filter_sort(&filter_compare); if (!quietflag) fprintf(stderr," done.\n"); // TODO remove duplicates } static void filters_clean() { #ifdef INTFILTER VEC_FREE(ifilters); #else VEC_FREE(bfilters); #endif } static size_t filters_count() { #ifdef INTFILTER return VEC_LENGTH(ifilters); #else return VEC_LENGTH(bfilters); #endif } #ifdef STATISTICS #define ADDNUMSUCCESS ++st->numsuccess.v #else #define ADDNUMSUCCESS do ; while (0) #endif #ifdef INTFILTER # ifndef BINSEARCH #define MATCHFILTER(it,pk) \ ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f) #define DOFILTER(it,pk,code) \ do { \ for (it = 0;it < VEC_LENGTH(ifilters);++it) { \ if (unlikely(MATCHFILTER(it,pk))) { \ code; \ break; \ } \ } \ } while (0) # else // BINSEARCH # ifdef OMITMASK #define DOFILTER(it,pk,code) \ do { \ register IFT maskedpk = *(IFT *)(pk) & ifiltermask; \ for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \ it = (up + down) / 2; \ if (maskedpk < VEC_BUF(ifilters,it).f) \ up = it; \ else if (maskedpk > VEC_BUF(ifilters,it).f) \ down = it + 1; \ else { \ code; \ break; \ } \ } \ } while (0) # else // OMITMASK #define DOFILTER(it,pk,code) \ do { \ for (size_t down = 0,up = VEC_LENGTH(ifilters);down < up;) { \ it = (up + down) / 2; \ IFT maskedpk = *(IFT *)(pk) & VEC_BUF(ifilters,it).m; \ register int cmp = memcmp(&maskedpk,&VEC_BUF(ifilters,it).f,sizeof(IFT)); \ if (cmp < 0) \ up = it; \ else if (cmp > 0) \ down = it + 1; \ else { \ code; \ break; \ } \ } \ } while (0) # endif // OMITMASK # endif // BINSEARCH #else // INTFILTER # ifndef BINSEARCH #define MATCHFILTER(it,pk) ( \ memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len) == 0 && \ (pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) == VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) #define DOFILTER(it,pk,code) \ do { \ for (it = 0;it < VEC_LENGTH(bfilters);++it) { \ if (unlikely(MATCHFILTER(it,pk))) { \ code; \ break; \ } \ } \ } while (0) # else // BINSEARCH #define DOFILTER(it,pk,code) \ do { \ for (size_t down = 0,up = VEC_LENGTH(bfilters);down < up;) { \ it = (up + down) / 2; \ { \ register int filterdiff = memcmp(pk,VEC_BUF(bfilters,it).f,VEC_BUF(bfilters,it).len); \ if (filterdiff < 0) { \ up = it; \ continue; \ } \ if (filterdiff > 0) { \ down = it + 1; \ continue; \ } \ } \ if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) < \ VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \ { \ up = it; \ continue; \ } \ if ((pk[VEC_BUF(bfilters,it).len] & VEC_BUF(bfilters,it).mask) > \ VEC_BUF(bfilters,it).f[VEC_BUF(bfilters,it).len]) \ { \ down = it + 1; \ continue; \ } \ { \ code; \ break; \ } \ } \ } while (0) # endif // BINSEARCH #endif // INTFILTER static void loadfilterfile(const char *fname) { char buf[128]; FILE *f = fopen(fname,"r"); while (fgets(buf,sizeof(buf),f)) { for (char *p = buf;*p;++p) { if (*p == '\n') { *p = 0; break; } } if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0) filters_add(buf); } } static void filters_print() { if (quietflag) return; size_t i,l; #ifdef INTFILTER l = VEC_LENGTH(ifilters); #else l = VEC_LENGTH(bfilters); #endif if (l) fprintf(stderr,"filters:\n"); for (i = 0;i < l;++i) { char buf0[256],buf1[256]; u8 bufx[128]; if (i >= 20) { size_t notshown = l - i; fprintf(stderr,"[another %zu %s not shown]\n", notshown,notshown == 1 ? "filter" : "filters"); break; } #ifdef INTFILTER size_t len = 0; u8 *imraw; # ifndef OMITMASK imraw = (u8 *)&VEC_BUF(ifilters,i).m; # else imraw = (u8 *)&ifiltermask; # endif while (len < sizeof(IFT) && imraw[len] != 0x00) ++len; u8 mask = imraw[len-1]; u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f; #else size_t len = VEC_BUF(bfilters,i).len + 1; u8 mask = VEC_BUF(bfilters,i).mask; u8 *ifraw = VEC_BUF(bfilters,i).f; #endif base32_to(buf0,ifraw,len); memcpy(bufx,ifraw,len); bufx[len - 1] |= ~mask; base32_to(buf1,bufx,len); char *a = buf0,*b = buf1; while (*a && *a == *b) ++a, ++b; *a = 0; fprintf(stderr,"\t%s\n",buf0); } fprintf(stderr,"in total, %zu %s\n",l,l == 1 ? "filter" : "filters"); } // statistics, if enabled #ifdef STATISTICS struct statstruct { union { u32 v; size_t align; } numcalc; union { u32 v; size_t align; } numsuccess; union { u32 v; size_t align; } numrestart; } ; VEC_STRUCT(statsvec,struct statstruct); struct tstatstruct { u64 numcalc; u64 numsuccess; u64 numrestart; u32 oldnumcalc; u32 oldnumsuccess; u32 oldnumrestart; } ; VEC_STRUCT(tstatsvec,struct tstatstruct); #endif static void onionready(char *sname, const u8 *secret, const u8 *pubonion) { if (endwork) return; if (numneedgenerate) { pthread_mutex_lock(&keysgenerated_mutex); if (keysgenerated >= numneedgenerate) { pthread_mutex_unlock(&keysgenerated_mutex); return; } } if (createdir(sname,1) != 0) { if (numneedgenerate) pthread_mutex_unlock(&keysgenerated_mutex); return; } if (numneedgenerate) { ++keysgenerated; if (keysgenerated >= numneedgenerate) endwork = 1; pthread_mutex_unlock(&keysgenerated_mutex); } strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); writetofile(sname,secret,skprefixlen + SECRET_LEN,1); strcpy(&sname[onionendpos],"/hostname"); FILE *hfile = fopen(sname,"w"); if (hfile) { sname[onionendpos] = '\n'; fwrite(&sname[direndpos],ONIONLEN + 1,1,hfile); fclose(hfile); } strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); writetofile(sname,pubonion,pkprefixlen + PUBLIC_LEN,0); if (fout) { sname[onionendpos] = '\n'; pthread_mutex_lock(&fout_mutex); fwrite(&sname[printstartpos],printlen,1,fout); fflush(fout); pthread_mutex_unlock(&fout_mutex); } } // little endian inc static void addsk32(u8 *sk) { register unsigned int c = 8; for (size_t i = 0;i < 32;++i) { c = (unsigned int)sk[i] + c; sk[i] = c & 0xFF; c >>= 8; // unsure if needed if (!c) break; } } // 0123 4567 xxxx --3--> 3456 7xxx // 0123 4567 xxxx --1--> 1234 567x static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits) { size_t i,sbytes = sbits / 8; sbits %= 8; for (i = 0;i + sbytes < PUBLIC_LEN;++i) { dst[i] = (src[i+sbytes] << sbits) | (src[i+sbytes+1] >> (8 - sbits)); } for(;i < PUBLIC_LEN;++i) dst[i] = 0; } static void *dowork(void *task) { union pubonionunion { u8 raw[pkprefixlen + PUBLIC_LEN + 32]; struct { u64 prefix[4]; u64 key[4]; u64 hash[4]; } i; } pubonion; u8 * const pk = &pubonion.raw[pkprefixlen]; u8 secret[skprefixlen + SECRET_LEN]; u8 * const sk = &secret[skprefixlen]; u8 seed[SEED_LEN]; u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1]; u8 wpk[PUBLIC_LEN + 1]; size_t i; char *sname; #ifdef STATISTICS struct statstruct *st = (struct statstruct *)task; #endif memcpy(secret,skprefix,skprefixlen); wpk[PUBLIC_LEN] = 0; memset(&pubonion,0,sizeof(pubonion)); memcpy(pubonion.raw,pkprefix,pkprefixlen); // write version later as it will be overwritten by hash memcpy(hashsrc,checksumstr,checksumstrlen); hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version sname = malloc(workdirlen + ONIONLEN + 63 + 1); if (!sname) abort(); if (workdir) memcpy(sname,workdir,workdirlen); initseed: randombytes(seed,sizeof(seed)); ed25519_seckey_expand(sk,seed); #ifdef STATISTICS ++st->numrestart.v; #endif again: if (unlikely(endwork)) goto end; ed25519_pubkey(pk,sk); #ifdef STATISTICS ++st->numcalc.v; #endif DOFILTER(i,pk,{ if (numwords > 1) { shiftpk(wpk,pk,filter_len(i)); size_t j; for (int w = 1;;) { DOFILTER(j,wpk,goto secondfind); goto next; secondfind: if (++w >= numwords) break; shiftpk(wpk,wpk,filter_len(j)); } } // sanity check if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31]) goto initseed; ADDNUMSUCCESS; // calc checksum memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN); FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]); // version byte pk[PUBLIC_LEN + 2] = 0x03; // base32 strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion"); onionready(sname,secret,pubonion.raw); pk[PUBLIC_LEN] = 0; goto initseed; }); next: addsk32(sk); goto again; end: free(sname); return 0; } static void addsztoscalar32(u8 *dst,size_t v) { int i; u32 c = 0; for (i = 0;i < 32;++i) { c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8; v >>= 8; ++dst; } } static void *dofastwork(void *task) { union pubonionunion { u8 raw[pkprefixlen + PUBLIC_LEN + 32]; struct { u64 prefix[4]; u64 key[4]; u64 hash[4]; } i; } pubonion; u8 * const pk = &pubonion.raw[pkprefixlen]; u8 secret[skprefixlen + SECRET_LEN]; u8 * const sk = &secret[skprefixlen]; u8 seed[SEED_LEN]; u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1]; u8 wpk[PUBLIC_LEN + 1]; ge_p3 ge_public; size_t counter; size_t i; char *sname; #ifdef STATISTICS struct statstruct *st = (struct statstruct *)task; #endif memcpy(secret, skprefix, skprefixlen); wpk[PUBLIC_LEN] = 0; memset(&pubonion,0,sizeof(pubonion)); memcpy(pubonion.raw,pkprefix,pkprefixlen); // write version later as it will be overwritten by hash memcpy(hashsrc,checksumstr,checksumstrlen); hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version sname = malloc(workdirlen + ONIONLEN + 63 + 1); if (!sname) abort(); if (workdir) memcpy(sname,workdir,workdirlen); initseed: #ifdef STATISTICS ++st->numrestart.v; #endif randombytes(seed,sizeof(seed)); ed25519_seckey_expand(sk,seed); ge_scalarmult_base(&ge_public,sk); ge_p3_tobytes(pk,&ge_public); for (counter = 0;counter < SIZE_MAX-8;counter += 8) { ge_p1p1 sum; if (unlikely(endwork)) goto end; DOFILTER(i,pk,{ if (numwords > 1) { shiftpk(wpk,pk,filter_len(i)); size_t j; for (int w = 1;;) { DOFILTER(j,wpk,goto secondfind); goto next; secondfind: if (++w >= numwords) break; shiftpk(wpk,wpk,filter_len(j)); } } // found! // update secret key with counter addsztoscalar32(sk,counter); // sanity check if ((sk[0] & 248) != sk[0] || ((sk[31] & 63) | 64) != sk[31]) goto initseed; ADDNUMSUCCESS; // calc checksum memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN); FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]); // version byte pk[PUBLIC_LEN + 2] = 0x03; // full name strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion"); onionready(sname,secret,pubonion.raw); pk[PUBLIC_LEN] = 0; // don't reuse same seed goto initseed; }); next: ge_add(&sum, &ge_public,&ge_eightpoint); ge_p1p1_to_p3(&ge_public,&sum); ge_p3_tobytes(pk,&ge_public); #ifdef STATISTICS ++st->numcalc.v; #endif } goto initseed; end: free(sname); return 0; } void printhelp(const char *progname) { fprintf(stderr, "Usage: %s filter [filter...] [options]\n" " %s -f filterfile [options]\n" "Options:\n" "\t-h - print help\n" "\t-f - instead of specifying filter(s) via commandline, specify filter file which contains filters separated by newlines\n" "\t-q - do not print diagnostic output to stderr\n" "\t-x - do not print onion names\n" "\t-o filename - output onion names to specified file\n" "\t-F - include directory names in onion names output\n" "\t-d dirname - output directory\n" "\t-t numthreads - specify number of threads (default - auto)\n" "\t-j numthreads - same as -t\n" "\t-n numkeys - specify number of keys (default - 0 - unlimited)\n" "\t-N numwords - specify number of words per key (default - 1)\n" "\t-z - use faster key generation method. this is now default\n" "\t-Z - use slower key generation method\n" "\t-s - print statistics each 10 seconds\n" "\t-S t - print statistics every specified ammount of seconds\n" "\t-T - do not reset statistics counters when printing\n" ,progname,progname); exit(1); } void setworkdir(const char *wd) { free(workdir); size_t l = strlen(wd); if (!l) { workdir = 0; workdirlen = 0; if (!quietflag) fprintf(stderr, "unset workdir\n"); return; } int needslash = 0; if (wd[l-1] != '/') needslash = 1; char *s = malloc(l + needslash + 1); if (!s) abort(); memcpy(s, wd, l); if (needslash) s[l++] = '/'; s[l] = 0; workdir = s; workdirlen = l; if (!quietflag) fprintf(stderr,"set workdir: %s\n",workdir); } VEC_STRUCT(threadvec, pthread_t); int main(int argc,char **argv) { char *outfile = 0; const char *arg; int ignoreargs = 0; int dirnameflag = 0; int numthreads = 0; int fastkeygen = 1; struct threadvec threads; #ifdef STATISTICS struct statsvec stats; struct tstatsvec tstats; u64 reportdelay = 0; int realtimestats = 1; #endif int tret; ge_initeightpoint(); filters_init(); fout = stdout; pthread_mutex_init(&keysgenerated_mutex, 0); pthread_mutex_init(&fout_mutex, 0); const char *progname = argv[0]; if (argc <= 1) printhelp(progname); argc--, argv++; while (argc--) { arg = *argv++; if (!ignoreargs && *arg == '-') { int numargit = 0; nextarg: ++arg; ++numargit; if (*arg == '-') { if (numargit > 1) { fprintf(stderr, "unrecognised argument: -\n"); exit(1); } ++arg; if (!*arg) ignoreargs = 1; else if (!strcmp(arg, "help")) printhelp(progname); else { fprintf(stderr, "unrecognised argument: --%s\n", arg); exit(1); } numargit = 0; } else if (*arg == 0) { if (numargit == 1) ignoreargs = 1; continue; } else if (*arg == 'h') printhelp(progname); else if (*arg == 'f') { if (argc--) loadfilterfile(*argv++); else { fprintf(stderr, "additional argument required\n"); exit(1); } } else if (*arg == 'q') ++quietflag; else if (*arg == 'x') fout = 0; else if (*arg == 'o') { if (argc--) outfile = *argv++; else { fprintf(stderr, "additional argument required\n"); exit(1); } } else if (*arg == 'F') dirnameflag = 1; else if (*arg == 'd') { if (argc--) { setworkdir(*argv++); } else { fprintf(stderr, "additional argument required\n"); } } else if (*arg == 't' || *arg == 'j') { if (argc--) numthreads = atoi(*argv++); else { fprintf(stderr, "additional argument required\n"); exit(1); } } else if (*arg == 'n') { if (argc--) numneedgenerate = (size_t)atoll(*argv++); else { fprintf(stderr, "additional argument required\n"); exit(1); } } else if (*arg == 'N') { if (argc--) numwords = atoi(*argv++); else { fprintf(stderr, "additional argument required\n"); exit(1); } } else if (*arg == 'Z') fastkeygen = 0; else if (*arg == 'z') fastkeygen = 1; else if (*arg == 's') { #ifdef STATISTICS reportdelay = 10000000; #else fprintf(stderr,"statistics support not compiled in\n"); exit(1); #endif } else if (*arg == 'S') { #ifdef STATISTICS if (argc--) reportdelay = (u64)atoll(*argv++) * 1000000; else { fprintf(stderr, "additional argument required\n"); exit(1); } #else fprintf(stderr,"statistics support not compiled in\n"); exit(1); #endif } else if (*arg == 'T') { #ifdef STATISTICS realtimestats = 0; #else fprintf(stderr,"statistics support not compiled in\n"); exit(1); #endif } else { fprintf(stderr, "unrecognised argument: -%c\n", *arg); exit(1); } if (numargit) goto nextarg; } else filters_add(arg); } filters_prepare(); filters_print(); #ifdef STATISTICS if (!filters_count() && !reportdelay) #else if (!filters_count()) #endif return 0; if (outfile) fout = fopen(outfile, "w"); if (workdir) createdir(workdir,1); direndpos = workdirlen; onionendpos = workdirlen + ONIONLEN; if (!dirnameflag) { printstartpos = direndpos; printlen = ONIONLEN + 1; } else { printstartpos = 0; printlen = onionendpos + 1; } if (numthreads <= 0) { numthreads = cpucount(); if (numthreads <= 0) numthreads = 1; if (!quietflag) fprintf(stderr,"using %d %s\n", numthreads,numthreads == 1 ? "thread" : "threads"); } signal(SIGTERM,termhandler); signal(SIGINT,termhandler); VEC_INIT(threads); VEC_ADDN(threads,numthreads); #ifdef STATISTICS VEC_INIT(stats); VEC_ADDN(stats,numthreads); VEC_ZERO(stats); VEC_INIT(tstats); VEC_ADDN(tstats,numthreads); VEC_ZERO(tstats); #endif for (size_t i = 0;i < VEC_LENGTH(threads);++i) { void *tp = 0; #ifdef STATISTICS tp = &VEC_BUF(stats,i); #endif tret = pthread_create(&VEC_BUF(threads,i),0,fastkeygen ? dofastwork : dowork,tp); if (tret) { fprintf(stderr,"error while making %zuth thread: %d\n",i,tret); exit(1); } } #ifdef STATISTICS struct timespec nowtime; u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0; if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) { fprintf(stderr, "failed to get time\n"); exit(1); } istarttime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000); #endif struct timespec ts; memset(&ts,0,sizeof(ts)); ts.tv_nsec = 100000000; while (!endwork) { if (numneedgenerate && keysgenerated >= numneedgenerate) { endwork = 1; break; } nanosleep(&ts,0); #ifdef STATISTICS clock_gettime(CLOCK_MONOTONIC,&nowtime); inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000); u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0; for (size_t i = 0;i < numthreads;++i) { u32 newt,tdiff; // numcalc newt = VEC_BUF(stats,i).numcalc.v; tdiff = newt - VEC_BUF(tstats,i).oldnumcalc; VEC_BUF(tstats,i).oldnumcalc = newt; VEC_BUF(tstats,i).numcalc += (u64)tdiff; sumcalc += VEC_BUF(tstats,i).numcalc; // numsuccess newt = VEC_BUF(stats,i).numsuccess.v; tdiff = newt - VEC_BUF(tstats,i).oldnumsuccess; VEC_BUF(tstats,i).oldnumsuccess = newt; VEC_BUF(tstats,i).numsuccess += (u64)tdiff; sumsuccess += VEC_BUF(tstats,i).numsuccess; // numrestart newt = VEC_BUF(stats,i).numrestart.v; tdiff = newt - VEC_BUF(tstats,i).oldnumrestart; VEC_BUF(tstats,i).oldnumrestart = newt; VEC_BUF(tstats,i).numrestart += (u64)tdiff; sumrestart += VEC_BUF(tstats,i).numrestart; } if (reportdelay && (!ireporttime || inowtime - ireporttime >= reportdelay)) { if (ireporttime) ireporttime += reportdelay; else ireporttime = inowtime; if (!ireporttime) ireporttime = 1; double calcpersec = (1000000.0 * sumcalc) / (inowtime - istarttime); double succpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime); double restpersec = (1000000.0 * sumrestart) / (inowtime - istarttime); fprintf(stderr,">calc/sec:%8lf, succ/sec:%8lf, rest/sec:%8lf, elapsed:%5.6lfsec\n", calcpersec,succpersec,restpersec, (inowtime - istarttime + elapsedoffset) / 1000000.0); if (realtimestats) { for (size_t i = 0;i < numthreads;++i) { VEC_BUF(tstats,i).numcalc = 0; VEC_BUF(tstats,i).numsuccess = 0; VEC_BUF(tstats,i).numrestart = 0; } elapsedoffset += inowtime - istarttime; istarttime = inowtime; } } if (sumcalc > U64_MAX / 2) { for (size_t i = 0;i < numthreads;++i) { VEC_BUF(tstats,i).numcalc /= 2; VEC_BUF(tstats,i).numsuccess /= 2; VEC_BUF(tstats,i).numrestart /= 2; } u64 timediff = (inowtime - istarttime + 1) / 2; elapsedoffset += timediff; istarttime += timediff; } #endif } if (!quietflag) fprintf(stderr, "waiting for threads to finish..."); for (size_t i = 0;i < VEC_LENGTH(threads);++i) pthread_join(VEC_BUF(threads,i),0); if (!quietflag) fprintf(stderr, " done.\n"); pthread_mutex_destroy(&keysgenerated_mutex); pthread_mutex_destroy(&fout_mutex); filters_clean(); if (outfile) fclose(fout); return 0; }