mkp224o/main.c
2019-02-13 23:35:36 +00:00

1207 lines
27 KiB
C

#ifdef __linux__
#define _POSIX_C_SOURCE 200112L
#endif
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <time.h>
#include <pthread.h>
#include <signal.h>
#include <sodium/core.h>
#include <sodium/randombytes.h>
#ifdef PASSPHRASE
#include <sodium/crypto_hash_sha256.h>
#include <sodium/crypto_pwhash.h>
#endif
#include <sodium/utils.h>
#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"
#include "common.h"
#include "yaml.h"
#ifndef _WIN32
#define FSZ "%zu"
#else
#define FSZ "%Iu"
#endif
// additional 0 terminator is added by C
static const char * const pkprefix = "== ed25519v1-public: type0 ==\0\0";
static const char * const skprefix = "== ed25519v1-secret: type0 ==\0\0";
static const char checksumstr[] = ".onion checksum";
#define checksumstrlen (sizeof(checksumstr) - 1) // 15
// output directory
static char *workdir = 0;
static size_t workdirlen = 0;
static int quietflag = 0;
static int verboseflag = 0;
#ifndef PCRE2FILTER
static int wantdedup = 0;
#endif
// 0, direndpos, onionendpos
// printstartpos = either 0 or direndpos
// printlen = either onionendpos + 1 or ONION_LEN + 1 (additional 1 is for newline)
size_t onionendpos; // end of .onion within string
size_t direndpos; // end of dir before .onion within string
size_t printstartpos; // where to start printing from
size_t printlen; // precalculated, related to printstartpos
static int yamloutput = 0;
static int numwords = 1;
static size_t numneedgenerate = 0;
static pthread_mutex_t keysgenerated_mutex;
static volatile size_t keysgenerated = 0;
static volatile int endwork = 0;
#ifdef PASSPHRASE
static pthread_mutex_t determseed_mutex;
static u8 determseed[SEED_LEN];
#endif
pthread_mutex_t fout_mutex;
FILE *fout;
static void termhandler(int sig)
{
switch (sig) {
case SIGTERM:
case SIGINT:
endwork = 1;
break;
}
}
#include "filters.h"
#ifdef STATISTICS
#define ADDNUMSUCCESS ++st->numsuccess.v
#else
#define ADDNUMSUCCESS do ; while (0)
#endif
// 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;
}
++keysgenerated;
if (keysgenerated == numneedgenerate)
endwork = 1;
pthread_mutex_unlock(&keysgenerated_mutex);
}
// Sanity check that the public key matches the private one.
ge_p3 point;
u8 testpk[PUBLIC_LEN];
ge_scalarmult_base(&point, secret);
ge_p3_tobytes(testpk, &point);
if (!memcmp(testpk, pubonion, PUBLIC_LEN))
abort();
if (!yamloutput) {
if (createdir(sname,1) != 0) {
pthread_mutex_lock(&fout_mutex);
fprintf(stderr,"ERROR: could not create directory for key output\n");
pthread_mutex_unlock(&fout_mutex);
return;
}
strcpy(&sname[onionendpos],"/hs_ed25519_secret_key");
writetofile(sname,secret,FORMATTED_SECRET_LEN,1);
strcpy(&sname[onionendpos],"/hs_ed25519_public_key");
writetofile(sname,pubonion,FORMATTED_PUBLIC_LEN,0);
strcpy(&sname[onionendpos],"/hostname");
FILE *hfile = fopen(sname,"w");
sname[onionendpos] = '\n';
if (hfile) {
fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile);
fclose(hfile);
}
if (fout) {
pthread_mutex_lock(&fout_mutex);
fwrite(&sname[printstartpos],printlen,1,fout);
fflush(fout);
pthread_mutex_unlock(&fout_mutex);
}
} else
yamlout_writekeys(&sname[direndpos],pubonion,secret);
}
union pubonionunion {
u8 raw[PKPREFIX_SIZE + PUBLIC_LEN + 32];
struct {
u64 prefix[4];
u64 key[4];
u64 hash[4];
} i;
} ;
static char *makesname()
{
char *sname = (char *) malloc(workdirlen + ONION_LEN + 63 + 1);
if (!sname)
abort();
if (workdir)
memcpy(sname,workdir,workdirlen);
return sname;
}
// 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] = (u8) ((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 pubonion;
u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
u8 secret[SKPREFIX_SIZE + SECRET_LEN];
u8 * const sk = &secret[SKPREFIX_SIZE];
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
PREFILTER
memcpy(secret,skprefix,SKPREFIX_SIZE);
wpk[PUBLIC_LEN] = 0;
memset(&pubonion,0,sizeof(pubonion));
memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
// write version later as it will be overwritten by hash
memcpy(hashsrc,checksumstr,checksumstrlen);
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
sname = makesname();
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; // what is this for?
goto initseed;
});
next:
addsk32(sk);
goto again;
end:
free(sname);
POSTFILTER
sodium_memzero(secret,sizeof(secret));
sodium_memzero(seed,sizeof(seed));
return 0;
}
// in little-endian order, 32 bytes aka 256 bits
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 pubonion;
u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
u8 secret[SKPREFIX_SIZE + SECRET_LEN];
u8 * const sk = &secret[SKPREFIX_SIZE];
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
PREFILTER
memcpy(secret,skprefix,SKPREFIX_SIZE);
wpk[PUBLIC_LEN] = 0;
memset(&pubonion,0,sizeof(pubonion));
memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
// write version later as it will be overwritten by hash
memcpy(hashsrc,checksumstr,checksumstrlen);
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
sname = makesname();
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; // what is this for?
// 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);
POSTFILTER
sodium_memzero(secret,sizeof(secret));
sodium_memzero(seed,sizeof(seed));
return 0;
}
#ifdef PASSPHRASE
static void reseedright(u8 sk[SECRET_LEN])
{
crypto_hash_sha256_state state;
crypto_hash_sha256_init(&state);
// old right side
crypto_hash_sha256_update(&state,&sk[32],32);
// new random data
randombytes(&sk[32],32);
crypto_hash_sha256_update(&state,&sk[32],32);
// put result in right side
crypto_hash_sha256_final(&state,&sk[32]);
}
static void *dofastworkdeterministic(void *task)
{
union pubonionunion pubonion;
u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
u8 secret[SKPREFIX_SIZE + SECRET_LEN];
u8 * const sk = &secret[SKPREFIX_SIZE];
u8 seed[SEED_LEN];
u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
u8 wpk[PUBLIC_LEN + 1];
ge_p3 ge_public;
size_t counter,oldcounter;
size_t i;
char *sname;
#ifdef STATISTICS
struct statstruct *st = (struct statstruct *)task;
#endif
PREFILTER
memcpy(secret,skprefix,SKPREFIX_SIZE);
wpk[PUBLIC_LEN] = 0;
memset(&pubonion,0,sizeof(pubonion));
memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
// write version later as it will be overwritten by hash
memcpy(hashsrc,checksumstr,checksumstrlen);
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
sname = makesname();
initseed:
pthread_mutex_lock(&determseed_mutex);
for (int i = 0; i < SEED_LEN; i++)
if (++determseed[i])
break;
memcpy(seed, determseed, SEED_LEN);
pthread_mutex_unlock(&determseed_mutex);
ed25519_seckey_expand(sk,seed);
// reseed right half of key with some random data to have more entropy
reseedright(sk);
#ifdef STATISTICS
++st->numrestart.v;
#endif
ge_scalarmult_base(&ge_public,sk);
ge_p3_tobytes(pk,&ge_public);
for (counter = oldcounter = 0;counter < DETERMINISTIC_LOOP_COUNT;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 delta since last hit (if any)
addsztoscalar32(sk,counter-oldcounter);
oldcounter = 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; // what is this for?
// reseed right half of key to avoid reuse, it won't change public key anyway
reseedright(sk);
});
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);
POSTFILTER
sodium_memzero(secret,sizeof(secret));
sodium_memzero(seed,sizeof(seed));
return 0;
}
#endif // PASSPHRASE
#ifndef BATCHNUM
#define BATCHNUM 2048
#endif
static void *dobatchwork(void *task)
{
union pubonionunion pubonion;
u8 * const pk = &pubonion.raw[PKPREFIX_SIZE];
u8 secret[SKPREFIX_SIZE + SECRET_LEN];
u8 * const sk = &secret[SKPREFIX_SIZE];
u8 seed[SEED_LEN];
u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
u8 wpk[PUBLIC_LEN + 1];
ge_p3 ge_public;
ge_p3 ge_batch[BATCHNUM];
fe *(batchgez)[BATCHNUM];
fe tmp_batch[BATCHNUM];
bytes32 pk_batch[BATCHNUM];
size_t counter;
size_t i;
char *sname;
#ifdef STATISTICS
struct statstruct *st = (struct statstruct *)task;
#endif
for (size_t b = 0;b < BATCHNUM;++b)
batchgez[b] = &GEZ(ge_batch[b]);
PREFILTER
memcpy(secret,skprefix,SKPREFIX_SIZE);
wpk[PUBLIC_LEN] = 0;
memset(&pubonion,0,sizeof(pubonion));
memcpy(pubonion.raw,pkprefix,PKPREFIX_SIZE);
// write version later as it will be overwritten by hash
memcpy(hashsrc,checksumstr,checksumstrlen);
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
sname = makesname();
initseed:
#ifdef STATISTICS
++st->numrestart.v;
#endif
randombytes(seed,sizeof(seed));
ed25519_seckey_expand(sk,seed);
ge_scalarmult_base(&ge_public,sk);
for (counter = 0;counter < SIZE_MAX-(8*BATCHNUM);counter += 8*BATCHNUM) {
ge_p1p1 sum;
if (unlikely(endwork))
goto end;
for (size_t b = 0;b < BATCHNUM;++b) {
ge_batch[b] = ge_public;
ge_add(&sum,&ge_public,&ge_eightpoint);
ge_p1p1_to_p3(&ge_public,&sum);
}
// NOTE: leaves unfinished
ge_p3_batchtobytes_destructive_1(pk_batch,ge_batch,batchgez,tmp_batch,BATCHNUM);
#ifdef STATISTICS
st->numcalc.v += BATCHNUM;
#endif
for (size_t b = 0;b < BATCHNUM;++b) {
DOFILTER(i,pk_batch[b],{
if (numwords > 1) {
shiftpk(wpk,pk_batch[b],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!
// finish it up
ge_p3_batchtobytes_destructive_finish(pk_batch[b],&ge_batch[b]);
// copy public key
memcpy(pk,pk_batch[b],PUBLIC_LEN);
// update secret key with counter
addsztoscalar32(sk,counter + (b * 8));
// 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; // what is this for?
// don't reuse same seed
goto initseed;
});
next:
;
}
}
goto initseed;
end:
free(sname);
POSTFILTER
sodium_memzero(secret,sizeof(secret));
sodium_memzero(seed,sizeof(seed));
return 0;
}
static void printhelp(FILE *out,const char *progname)
{
fprintf(out,
"Usage: %s filter [filter...] [options]\n"
" %s -f filterfile [options]\n"
"Options:\n"
"\t-h - print help to stdout and quit\n"
"\t-f - specify filter file which contains filters separated by newlines\n"
"\t-D - deduplicate filters\n"
"\t-q - do not print diagnostic output to stderr\n"
"\t-x - do not print onion names\n"
"\t-v - print more diagnostic data\n"
"\t-o filename - output onion names to specified file (append)\n"
"\t-O filename - output onion names to specified file (overwrite)\n"
"\t-F - include directory names in onion names output\n"
"\t-d dirname - output directory\n"
"\t-t numthreads - specify number of threads to utilise (default - CPU core count or 1)\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-B - use batching key generation method (>10x faster than -z, experimental)\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"
"\t-y - output generated keys in YAML format instead of dumping them to filesystem\n"
"\t-Y [filename [host.onion]] - parse YAML encoded input and extract key(s) to filesystem\n"
#ifdef PASSPHRASE
"\t-p passphrase - use passphrase to initialize the random seed with\n"
#endif
,progname,progname);
fflush(out);
}
static void e_additional()
{
fprintf(stderr,"additional argument required\n");
exit(1);
}
#ifndef STATISTICS
static void e_nostatistics()
{
fprintf(stderr,"statistics support not compiled in\n");
exit(1);
}
#endif
static 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;
}
unsigned needslash = 0;
if (wd[l-1] != '/')
needslash = 1;
char *s = (char *) 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)
{
const char *outfile = 0;
const char *infile = 0;
const char *hostname = 0;
const char *arg;
int ignoreargs = 0;
int dirnameflag = 0;
int numthreads = 0;
int fastkeygen = 1;
int batchkeygen = 0;
int yamlinput = 0;
#ifdef PASSPHRASE
int deterministic = 0;
#endif
int outfileoverwrite = 0;
struct threadvec threads;
#ifdef STATISTICS
struct statsvec stats;
struct tstatsvec tstats;
u64 reportdelay = 0;
int realtimestats = 1;
#endif
int tret;
if (sodium_init() < 0) {
fprintf(stderr,"sodium_init() failed\n");
return 1;
}
ge_initeightpoint();
filters_init();
setvbuf(stderr,0,_IONBF,0);
fout = stdout;
const char *progname = argv[0];
if (argc <= 1) {
printhelp(stderr,progname);
exit(1);
}
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") || !strcmp(arg,"usage")) {
printhelp(stdout,progname);
exit(0);
}
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(stdout,progname);
exit(0);
}
else if (*arg == 'f') {
if (argc--)
loadfilterfile(*argv++);
else
e_additional();
}
else if (*arg == 'D') {
#ifndef PCRE2FILTER
wantdedup = 1;
#else
fprintf(stderr,"WARNING: deduplication isn't supported with regex filters\n");
#endif
}
else if (*arg == 'q')
++quietflag;
else if (*arg == 'x')
fout = 0;
else if (*arg == 'v')
verboseflag = 1;
else if (*arg == 'o') {
outfileoverwrite = 0;
if (argc--)
outfile = *argv++;
else
e_additional();
}
else if (*arg == 'O') {
outfileoverwrite = 1;
if (argc--)
outfile = *argv++;
else
e_additional();
}
else if (*arg == 'F')
dirnameflag = 1;
else if (*arg == 'd') {
if (argc--)
setworkdir(*argv++);
else
e_additional();
}
else if (*arg == 't' || *arg == 'j') {
if (argc--)
numthreads = atoi(*argv++);
else
e_additional();
}
else if (*arg == 'n') {
if (argc--)
numneedgenerate = (size_t)atoll(*argv++);
else
e_additional();
}
else if (*arg == 'N') {
if (argc--)
numwords = atoi(*argv++);
else
e_additional();
}
else if (*arg == 'Z')
fastkeygen = 0;
else if (*arg == 'z')
fastkeygen = 1;
else if (*arg == 'B')
batchkeygen = 1;
else if (*arg == 's') {
#ifdef STATISTICS
reportdelay = 10000000;
#else
e_nostatistics();
#endif
}
else if (*arg == 'S') {
#ifdef STATISTICS
if (argc--)
reportdelay = (u64)atoll(*argv++) * 1000000;
else
e_additional();
#else
e_nostatistics();
#endif
}
else if (*arg == 'T') {
#ifdef STATISTICS
realtimestats = 0;
#else
e_nostatistics();
#endif
}
else if (*arg == 'y')
yamloutput = 1;
else if (*arg == 'Y') {
yamlinput = 1;
if (argc) {
--argc;
infile = *argv++;
if (!*infile)
infile = 0;
if (argc) {
--argc;
hostname = *argv++;
if (!*hostname)
hostname = 0;
if (hostname && strlen(hostname) != ONION_LEN) {
fprintf(stderr,"bad onion argument length\n");
exit(1);
}
}
}
#ifdef PASSPHRASE
} else if (*arg == 'p') {
if (argc--) {
static unsigned char salt[crypto_pwhash_SALTBYTES] = {0};
const char *phrase = *argv;
if (!strcmp(phrase, "@")) {
phrase = getenv("PASSPHRASE");
if (phrase == NULL) {
fprintf(stderr, "store passphrase in PASSPHRASE environment variable\n");
exit(1);
}
}
deterministic = 1;
fprintf(stderr, "expanding passphrase..."); fflush(stderr);
if (crypto_pwhash(determseed, sizeof(determseed),
phrase, strlen(phrase), salt,
PWHASH_OPSLIMIT, PWHASH_MEMLIMIT, PWHASH_ALG)) {
fprintf(stderr, " out of memory!\n");
exit(1);
}
fprintf(stderr, " done.\n");
argv++;
} else
e_additional();
#endif
}
else {
fprintf(stderr,"unrecognised argument: -%c\n",*arg);
exit(1);
}
if (numargit)
goto nextarg;
}
else
filters_add(arg);
}
if (outfile) {
fout = fopen(outfile,!outfileoverwrite ? "a" : "w");
if (!fout) {
perror("failed to open output file");
exit(1);
}
}
if (!fout && yamloutput) {
fprintf(stderr,"nil output with yaml mode does not make sense\n");
exit(1);
}
if (workdir)
createdir(workdir,1);
direndpos = workdirlen;
onionendpos = workdirlen + ONION_LEN;
if (!dirnameflag) {
printstartpos = direndpos;
printlen = ONION_LEN + 1; // + '\n'
} else {
printstartpos = 0;
printlen = onionendpos + 1; // + '\n'
}
if (yamlinput) {
char *sname = makesname();
FILE *fin = stdin;
if (infile) {
fin = fopen(infile,"r");
if (!fin) {
fprintf(stderr,"failed to open input file\n");
return 1;
}
}
tret = yamlin_parseandcreate(fin,sname,hostname);
if (infile) {
fclose(fin);
fin = 0;
}
free(sname);
if (tret)
return tret;
goto done;
}
filters_prepare();
filters_print();
#ifdef STATISTICS
if (!filters_count() && !reportdelay)
#else
if (!filters_count())
#endif
return 0;
#ifdef EXPANDMASK
if (numwords > 1 && flattened)
fprintf(stderr,"WARNING: -N switch will produce bogus results because we can't know filter width. reconfigure with --enable-besort and recompile.\n");
#endif
if (yamloutput)
yamlout_init();
pthread_mutex_init(&keysgenerated_mutex,0);
pthread_mutex_init(&fout_mutex,0);
#ifdef PASSPHRASE
pthread_mutex_init(&determseed_mutex,0);
#endif
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
#if 0
pthread_attr_t tattr,*tattrp = &tattr;
tret = pthread_attr_init(tattrp);
if (tret) {
perror("pthread_attr_init");
tattrp = 0;
}
else {
tret = pthread_attr_setstacksize(tattrp,80<<10);
if (tret)
perror("pthread_attr_setstacksize");
}
#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,
#ifdef PASSPHRASE
deterministic ? dofastworkdeterministic :
#endif
batchkeygen ? dobatchwork :
(fastkeygen ? dofastwork : dowork),tp);
if (tret) {
fprintf(stderr,"error while making " FSZ "th thread: %s\n",i,strerror(tret));
exit(1);
}
}
#if 0
if (tattrp) {
tret = pthread_attr_destroy(tattrp);
if (tret)
perror("pthread_attr_destroy");
}
#endif
#ifdef STATISTICS
struct timespec nowtime;
u64 istarttime,inowtime,ireporttime = 0,elapsedoffset = 0;
if (clock_gettime(CLOCK_MONOTONIC,&nowtime) < 0) {
perror("failed to get time");
exit(1);
}
istarttime = (1000000 * (u64)nowtime.tv_sec) + ((u64)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) + ((u64)nowtime.tv_nsec / 1000);
u64 sumcalc = 0,sumsuccess = 0,sumrestart = 0;
for (int 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 (int 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 (int 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");
if (yamloutput)
yamlout_clean();
#ifdef PASSPHRASE
pthread_mutex_destroy(&determseed_mutex);
#endif
pthread_mutex_destroy(&fout_mutex);
pthread_mutex_destroy(&keysgenerated_mutex);
done:
filters_clean();
if (outfile)
fclose(fout);
return 0;
}