mkp224o/main.c

906 lines
20 KiB
C
Raw Normal View History

2017-10-06 03:21:00 +02:00
#ifdef __linux__
#define _POSIX_C_SOURCE 200112L
#endif
2017-09-24 21:13:16 +02:00
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
2017-09-25 19:49:47 +02:00
#include <assert.h>
#include <time.h>
2017-09-24 21:13:16 +02:00
#include <pthread.h>
2017-09-25 19:49:47 +02:00
#include <signal.h>
2017-09-24 21:13:16 +02:00
#include <sys/stat.h>
2017-09-27 20:38:15 +02:00
#include <sodium/randombytes.h>
2017-09-24 21:13:16 +02:00
#include "types.h"
2017-09-27 20:38:15 +02:00
#include "likely.h"
2017-09-24 21:13:16 +02:00
#include "vec.h"
#include "base32.h"
2017-10-06 02:14:33 +02:00
#include "cpucount.h"
2017-09-24 21:13:16 +02:00
#include "keccak.h"
2017-10-02 16:11:23 +02:00
#include "ed25519/ed25519.h"
2017-09-24 21:13:16 +02:00
// additional leading zero 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;
2017-09-25 19:49:47 +02:00
static FILE *fout;
static size_t numneedgenerate = 0;
static pthread_mutex_t keysgenerated_mutex;
2017-09-24 21:13:16 +02:00
static volatile size_t keysgenerated = 0;
2017-09-25 19:49:47 +02:00
static volatile int endwork = 0;
static void termhandler(int sig)
{
switch (sig) {
case SIGTERM:
case SIGINT:
endwork = 1;
break;
}
}
2017-09-24 21:13:16 +02:00
2017-09-30 05:40:12 +02:00
// filters stuff
2017-09-24 23:52:17 +02:00
struct binfilter {
2017-09-25 22:57:27 +02:00
u8 f[PUBLIC_LEN];
2017-09-24 23:52:17 +02:00
size_t len; // real len minus one
u8 mask;
} ;
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
2017-10-08 23:15:08 +02:00
#ifndef IFT
#define IFT u64
#endif
2017-09-27 20:07:33 +02:00
struct intfilter {
2017-10-08 23:15:08 +02:00
IFT f,m;
2017-09-27 20:07:33 +02:00
} ;
VEC_STRUCT(ifiltervec,struct intfilter) ifilters;
#else
VEC_STRUCT(bfiltervec,struct binfilter) bfilters;
#endif
2017-09-24 23:52:17 +02:00
2017-09-24 21:13:16 +02:00
static void filters_init()
{
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
VEC_INIT(ifilters);
#else
2017-09-24 23:52:17 +02:00
VEC_INIT(bfilters);
2017-09-27 20:07:33 +02:00
#endif
2017-09-24 21:13:16 +02:00
}
#ifdef INTFILTER
// o - old filter, n - new
// return -1 - old stays, 0 - no conflict, 1 - new overrides old
// assumes masked bits are cleared already
static inline int ifilter_conflict(struct intfilter *o,struct intfilter *n)
{
if ((o->f & n->m) != (n->f & o->m))
return 0;
// determine which filter contain less bits
// this shit needs code independent of endianess, so not direct <=
if (memcmp(&o->m,&n->m,sizeof(o->m)) <= 0)
return -1;
return 1;
}
#else
// o - old filter, n - new
// return: -1 - old stays, 0 - no conflict, 1 - new overrides old
// assumes irrelevant bits are cleared already
static inline int bfilter_conflict(struct binfilter *o,struct binfilter *n)
{
for (size_t i = 0;i < sizeof(o->f);++i) {
u8 oo,nn;
if (i < n->len)
oo = o->f[i];
else if (i == n->len)
oo = o->f[i] & n->mask;
else
oo = 0;
if (i < o->len)
nn = n->f[i];
else if (i == o->len)
nn = n->f[i] & o->mask;
else
nn = 0;
if (oo != nn)
return 0;
}
// functional filters subset was the same
// determine which filter contain less bits
if (o->len < n->len)
return -1;
if (o->len > n->len)
return 1;
if (o->mask <= n->mask)
return -1;
return 1;
}
#endif
2017-09-24 21:13:16 +02:00
static void filters_add(const char *filter)
{
2017-09-24 23:52:17 +02:00
struct binfilter bf;
2017-09-25 19:49:47 +02:00
size_t ret, ret2;
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
union intconv {
2017-10-08 23:15:08 +02:00
IFT i;
u8 b[sizeof(IFT)];
2017-09-27 20:07:33 +02:00
} fc,mc;
#endif
2017-09-24 23:52:17 +02:00
memset(&bf,0,sizeof(bf));
2017-09-25 19:49:47 +02:00
if (!base32_valid(filter,&ret)) {
fprintf(stderr, "filter \"%s\" is invalid\n", filter);
return;
}
ret = BASE32_FROM_LEN(ret);
2017-09-24 23:52:17 +02:00
if (!ret)
return;
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
2017-10-08 23:15:08 +02:00
if (ret > sizeof(IFT))
2017-09-27 20:07:33 +02:00
#else
if (ret > PUBLIC_LEN)
#endif
{
2017-09-25 19:49:47 +02:00
fprintf(stderr, "filter \"%s\" is too long\n", filter);
return;
}
ret2 = base32_from(bf.f,&bf.mask,filter);
assert(ret == ret2);
2017-09-24 23:52:17 +02:00
bf.len = ret - 1;
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
mc.i = 0;
for (size_t i = 0;i < bf.len;++i)
mc.b[i] = 0xFF;
mc.b[bf.len] = bf.mask;
2017-10-08 23:15:08 +02:00
memcpy(fc.b,bf.f,sizeof(fc.b));
2017-09-27 20:07:33 +02:00
fc.i &= mc.i;
struct intfilter ifltr = {.f = fc.i,.m = mc.i};
VEC_FOR(ifilters,i) {
int c = ifilter_conflict(&VEC_BUF(ifilters,i),&ifltr);
if (c < 0)
return; // old filter eats us
else if (c > 0) {
VEC_REMOVE(ifilters,i);
--i;
// we eat old filter
}
}
2017-09-27 20:07:33 +02:00
VEC_ADD(ifilters,ifltr);
#else
VEC_FOR(bfilters,i) {
int c = bfilter_conflict(&VEC_BUF(bfilters,i),&bf);
if (c < 0)
return; // old filter eats us
else if (c > 0) {
VEC_REMOVE(bfilters,i);
--i;
// we eat old filter
}
}
2017-09-24 23:52:17 +02:00
VEC_ADD(bfilters,bf);
2017-09-27 20:07:33 +02:00
#endif
2017-09-24 21:13:16 +02:00
}
static void filters_clean()
{
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
VEC_FREE(ifilters);
#else
2017-09-24 23:52:17 +02:00
VEC_FREE(bfilters);
2017-09-27 20:07:33 +02:00
#endif
2017-09-24 21:13:16 +02:00
}
2017-09-25 22:57:27 +02:00
static size_t filters_count()
{
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
return VEC_LENGTH(ifilters);
#else
2017-09-25 22:57:27 +02:00
return VEC_LENGTH(bfilters);
2017-09-27 20:07:33 +02:00
#endif
2017-09-25 22:57:27 +02:00
}
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
#define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(ifilters);++it)
2017-10-08 23:15:08 +02:00
#define MATCHFILTER(it,pk) ((*(IFT *)(pk) & VEC_BUF(ifilters,it).m) == VEC_BUF(ifilters,it).f)
2017-09-27 20:07:33 +02:00
#else
#define FILTERFOR(it) for (it = 0;it < VEC_LENGTH(bfilters);++it)
#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])
#endif
2017-09-24 21:13:16 +02:00
static void loadfilterfile(const char *fname)
{
char buf[128];
2017-09-30 05:40:12 +02:00
FILE *f = fopen(fname,"r");
while (fgets(buf,sizeof(buf),f)) {
for (char *p = buf;*p;++p) {
2017-10-06 02:14:33 +02:00
if (*p == '\n') {
2017-09-24 21:13:16 +02:00
*p = 0;
2017-09-30 05:40:12 +02:00
break;
}
}
if (*buf && *buf != '#' && memcmp(buf,"//",2) != 0)
2017-09-24 21:13:16 +02:00
filters_add(buf);
}
}
static void printfilters()
{
2017-09-27 20:07:33 +02:00
size_t i,l;
#ifdef INTFILTER
l = VEC_LENGTH(ifilters);
#else
l = VEC_LENGTH(bfilters);
#endif
2017-09-25 19:49:47 +02:00
if (l)
fprintf(stderr, "filters:\n");
else
fprintf(stderr, "no filters defined\n");
for (i = 0;i < l;++i) {
2017-09-25 01:18:23 +02:00
char buf0[256],buf1[256];
u8 bufx[128];
2017-09-27 20:07:33 +02:00
#ifdef INTFILTER
size_t len = 0;
u8 *imraw = (u8 *)&VEC_BUF(ifilters,i).m;
2017-10-08 23:15:08 +02:00
while (len < sizeof(IFT) && imraw[len] != 0x00) ++len;
2017-09-27 20:07:33 +02:00
u8 mask = imraw[len-1];
u8 *ifraw = (u8 *)&VEC_BUF(ifilters,i).f;
#else
2017-09-25 01:18:23 +02:00
size_t len = VEC_BUF(bfilters,i).len + 1;
2017-09-27 20:07:33 +02:00
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;
2017-09-25 01:18:23 +02:00
base32_to(buf1,bufx,len);
char *a = buf0,*b = buf1;
while (*a && *a == *b)
++a, ++b;
*a = 0;
fprintf(stderr, "\t%s\n",buf0);
2017-09-24 23:52:17 +02:00
}
2017-09-24 21:13:16 +02:00
}
2017-09-30 05:40:12 +02:00
// statistics, if enabled
#ifdef STATISTICS
struct statstruct {
union {
2017-10-06 02:25:51 +02:00
u32 v;
size_t align;
} numcalc;
2017-09-30 05:40:12 +02:00
union {
2017-10-06 02:25:51 +02:00
u32 v;
size_t align;
} numsuccess;
2017-09-30 05:40:12 +02:00
} ;
VEC_STRUCT(statsvec,struct statstruct);
struct tstatstruct {
u64 numcalc;
u64 numsuccess;
u32 oldnumcalc;
u32 oldnumsuccess;
} ;
VEC_STRUCT(tstatsvec,struct tstatstruct);
#endif
2017-09-24 21:13:16 +02:00
static void onionready(char *sname, const u8 *secret, const u8 *pubonion)
{
FILE *fh;
2017-09-25 19:49:47 +02:00
if (endwork)
2017-09-24 21:13:16 +02:00
return;
2017-09-25 19:49:47 +02:00
if (numneedgenerate) {
pthread_mutex_lock(&keysgenerated_mutex);
if (keysgenerated >= numneedgenerate) {
pthread_mutex_unlock(&keysgenerated_mutex);
return;
}
}
2017-09-30 05:40:12 +02:00
if (mkdir(sname,0700) != 0) {
2017-09-25 19:49:47 +02:00
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");
fh = fopen(sname, "wb");
if (fh) {
fwrite(secret, skprefixlen + SECRET_LEN, 1, fh);
fclose(fh);
}
2017-09-24 21:13:16 +02:00
strcpy(&sname[onionendpos], "/hostname");
fh = fopen(sname, "w");
if (fh) {
sname[onionendpos] = '\n';
fwrite(&sname[direndpos], ONIONLEN+1, 1, fh);
fclose(fh);
}
strcpy(&sname[onionendpos], "/hs_ed25519_public_key");
fh = fopen(sname, "wb");
if (fh) {
fwrite(pubonion, pkprefixlen + PUBLIC_LEN, 1, fh);
fclose(fh);
}
if (fout) {
2017-09-30 05:40:12 +02:00
sname[onionendpos] = '\n';
2017-09-25 19:49:47 +02:00
pthread_mutex_lock(&fout_mutex);
2017-09-24 21:13:16 +02:00
fwrite(&sname[printstartpos], printlen, 1, fout);
fflush(fout);
2017-09-25 19:49:47 +02:00
pthread_mutex_unlock(&fout_mutex);
2017-09-24 21:13:16 +02:00
}
}
// little endian inc
static void addseed(u8 *seed)
{
register unsigned int c = 1;
2017-09-30 05:40:12 +02:00
for (size_t i = 0;i < SEED_LEN;++i) {
2017-09-24 21:13:16 +02:00
c = (unsigned int)seed[i] + c; seed[i] = c & 0xFF; c >>= 8;
// unsure if needed
if (!c) break;
}
}
static void *dowork(void *task)
{
2017-09-27 21:04:41 +02:00
union pubonionunion {
u8 raw[pkprefixlen + PUBLIC_LEN + 32];
struct {
u64 prefix[4];
u64 key[4];
u64 hash[4];
2017-10-06 02:30:49 +02:00
} i;
2017-09-27 21:04:41 +02:00
} pubonion;
u8 * const pk = &pubonion.raw[pkprefixlen];
2017-09-24 21:13:16 +02:00
u8 secret[skprefixlen + SECRET_LEN];
2017-09-24 23:52:17 +02:00
u8 * const sk = &secret[skprefixlen];
2017-09-24 21:13:16 +02:00
u8 seed[SEED_LEN];
u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
size_t i;
char *sname;
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
struct statstruct *st = (struct statstruct *)task;
#endif
2017-09-24 21:13:16 +02:00
2017-09-24 23:52:17 +02:00
memcpy(secret,skprefix,skprefixlen);
2017-09-27 21:04:41 +02:00
memcpy(pubonion.raw,pkprefix,pkprefixlen);
2017-09-29 20:18:41 +02:00
// write version later as it will be overwritten by hash
2017-09-24 23:52:17 +02:00
memcpy(hashsrc,checksumstr,checksumstrlen);
2017-09-24 21:13:16 +02:00
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
2017-09-25 19:49:47 +02:00
sname = malloc(workdirlen + ONIONLEN + 63 + 1);
2017-09-24 21:13:16 +02:00
if (workdir)
2017-09-24 23:52:17 +02:00
memcpy(sname,workdir,workdirlen);
2017-09-24 21:13:16 +02:00
initseed:
2017-09-24 23:52:17 +02:00
randombytes(seed,sizeof(seed));
2017-09-24 21:13:16 +02:00
again:
2017-09-27 20:38:15 +02:00
if (unlikely(endwork))
2017-09-24 21:13:16 +02:00
goto end;
ed25519_seckey_expand(sk,seed);
ed25519_pubkey(pk,sk);
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
2017-10-06 02:25:51 +02:00
++st->numcalc.v;
2017-09-30 05:40:12 +02:00
#endif
2017-09-27 20:07:33 +02:00
FILTERFOR(i) {
2017-09-27 20:38:15 +02:00
if (unlikely(MATCHFILTER(i,pk))) {
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
2017-10-06 02:25:51 +02:00
++st->numsuccess.v;
2017-09-30 05:40:12 +02:00
#endif
2017-09-29 20:18:41 +02:00
// calc checksum
2017-09-27 21:04:41 +02:00
memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
2017-09-29 20:18:41 +02:00
// version byte
pk[PUBLIC_LEN + 2] = 0x03;
// base32
2017-09-27 21:04:41 +02:00
strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN), ".onion");
onionready(sname, secret, pubonion.raw);
2017-09-24 21:13:16 +02:00
goto initseed;
}
}
addseed(seed);
goto again;
end:
2017-09-25 19:49:47 +02:00
free(sname);
2017-09-24 21:13:16 +02:00
return 0;
}
2017-09-30 05:40:12 +02:00
static void addu64toscalar32(u8 *dst,u64 v)
2017-09-24 21:13:16 +02:00
{
int i;
u32 c = 0;
for (i = 0;i < 32;++i) {
2017-09-30 05:40:12 +02:00
c += *dst + (v & 0xFF); *dst = c & 0xFF; c >>= 8;
2017-09-24 21:13:16 +02:00
v >>= 8;
2017-09-30 05:40:12 +02:00
++dst;
2017-09-24 21:13:16 +02:00
}
}
static void *dofastwork(void *task)
{
2017-09-27 21:04:41 +02:00
union pubonionunion {
u8 raw[pkprefixlen + PUBLIC_LEN + 32];
struct {
u64 prefix[4];
u64 key[4];
u64 hash[4];
2017-10-06 02:30:49 +02:00
} i;
2017-09-27 21:04:41 +02:00
} pubonion;
u8 * const pk = &pubonion.raw[pkprefixlen];
2017-09-24 21:13:16 +02:00
u8 secret[skprefixlen + SECRET_LEN];
u8 * const sk = &secret[skprefixlen];
u8 seed[SEED_LEN];
u8 hashsrc[checksumstrlen + PUBLIC_LEN + 1];
ge_p3 ge_public;
u64 counter;
size_t i;
char *sname;
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
struct statstruct *st = (struct statstruct *)task;
#endif
2017-09-24 21:13:16 +02:00
memcpy(secret, skprefix, skprefixlen);
2017-09-27 21:04:41 +02:00
memcpy(pubonion.raw, pkprefix, pkprefixlen);
2017-09-29 20:18:41 +02:00
// write version later as it will be overwritten by hash
2017-09-24 21:13:16 +02:00
memcpy(hashsrc, checksumstr, checksumstrlen);
hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version
2017-09-25 19:49:47 +02:00
sname = malloc(workdirlen + ONIONLEN + 63 + 1);
2017-09-24 21:13:16 +02:00
if (workdir)
memcpy(sname, workdir, workdirlen);
initseed:
2017-09-24 23:52:17 +02:00
randombytes(seed,sizeof(seed));
ed25519_seckey_expand(sk,seed);
2017-09-24 21:13:16 +02:00
ge_scalarmult_base(&ge_public,sk);
2017-09-24 23:52:17 +02:00
ge_p3_tobytes(pk,&ge_public);
2017-09-24 21:13:16 +02:00
for (counter = 0;counter < U64_MAX-8;counter += 8) {
ge_p1p1 sum;
2017-09-27 20:38:15 +02:00
if (unlikely(endwork))
2017-09-24 21:13:16 +02:00
goto end;
2017-09-27 20:07:33 +02:00
FILTERFOR(i) {
2017-09-27 20:38:15 +02:00
if (unlikely(MATCHFILTER(i,pk))) {
2017-09-24 21:13:16 +02:00
// found!
// update secret key with counter
2017-09-27 20:07:33 +02:00
addu64toscalar32(sk,counter);
2017-09-24 21:13:16 +02:00
// sanity check
if (((sk[0] & 248) == sk[0]) && (((sk[31] & 63) | 64) == sk[31])) {
/* These operations should be a no-op. */
sk[0] &= 248;
sk[31] &= 63;
sk[31] |= 64;
}
else goto initseed;
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
2017-10-06 02:25:51 +02:00
++st->numsuccess.v;
2017-09-30 05:40:12 +02:00
#endif
2017-09-24 21:13:16 +02:00
// calc checksum
2017-09-24 23:52:17 +02:00
memcpy(&hashsrc[checksumstrlen],pk,PUBLIC_LEN);
FIPS202_SHA3_256(hashsrc,sizeof(hashsrc),&pk[PUBLIC_LEN]);
2017-09-29 20:18:41 +02:00
// version byte
pk[PUBLIC_LEN + 2] = 0x03;
2017-09-24 21:13:16 +02:00
// full name
2017-09-24 23:52:17 +02:00
strcpy(base32_to(&sname[direndpos],pk,PUBONION_LEN),".onion");
2017-09-27 21:04:41 +02:00
onionready(sname,secret,pubonion.raw);
2017-09-24 21:13:16 +02:00
// don't reuse same seed
goto initseed;
}
}
// next
2017-09-24 23:52:17 +02:00
ge_add(&sum, &ge_public,&ge_eightpoint);
ge_p1p1_to_p3(&ge_public,&sum);
ge_p3_tobytes(pk,&ge_public);
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
2017-10-06 02:25:51 +02:00
++st->numcalc.v;
2017-09-30 05:40:12 +02:00
#endif
2017-09-24 21:13:16 +02:00
}
goto initseed;
end:
2017-09-25 19:49:47 +02:00
free(sname);
2017-09-24 21:13:16 +02:00
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"
2017-09-30 05:40:12 +02:00
"\t-j numthreads - same as -t\n"
2017-09-24 21:13:16 +02:00
"\t-n numkeys - specify number of keys (default - 0 - unlimited)\n"
2017-09-30 14:43:32 +02:00
"\t-z - use faster key generation method. this is now default\n"
"\t-Z - use slower key generation method\n"
2017-09-30 05:40:12 +02:00
"\t-s - print statistics each 10 seconds\n"
"\t-S t - print statistics every specified ammount of seconds\n"
2017-09-30 14:43:32 +02:00
"\t-T - do not reset statistics counters when printing\n"
2017-09-24 21:13:16 +02:00
,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);
memcpy(s, wd, l);
if (needslash)
s[l++] = '/';
s[l] = 0;
workdir = s;
workdirlen = l;
if (!quietflag)
2017-09-30 05:40:12 +02:00
fprintf(stderr,"set workdir: %s\n",workdir);
2017-09-24 21:13:16 +02:00
}
VEC_STRUCT(threadvec, pthread_t);
2017-09-30 05:40:12 +02:00
int main(int argc,char **argv)
2017-09-24 21:13:16 +02:00
{
char *outfile = 0;
const char *arg;
int ignoreargs = 0;
int dirnameflag = 0;
int numthreads = 0;
2017-09-30 14:43:32 +02:00
int fastkeygen = 1;
2017-09-24 21:13:16 +02:00
struct threadvec threads;
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
struct statsvec stats;
struct tstatsvec tstats;
u64 reportdelay = 0;
2017-09-30 14:43:32 +02:00
int realtimestats = 1;
2017-09-30 05:46:17 +02:00
#endif
2017-09-24 21:13:16 +02:00
int tret;
2017-10-02 16:11:23 +02:00
ge_initeightpoint();
2017-09-24 21:13:16 +02:00
filters_init();
2017-09-24 21:13:16 +02:00
fout = stdout;
2017-09-25 19:49:47 +02:00
pthread_mutex_init(&keysgenerated_mutex, 0);
2017-09-24 21:13:16 +02:00
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");
}
}
2017-09-30 05:40:12 +02:00
else if (*arg == 't' || *arg == 'j') {
2017-09-24 21:13:16 +02:00
if (argc--)
numthreads = atoi(*argv++);
else {
fprintf(stderr, "additional argument required\n");
exit(1);
}
}
2017-09-25 19:49:47 +02:00
else if (*arg == 'n') {
if (argc--)
numneedgenerate = (size_t)atoll(*argv++);
else {
fprintf(stderr, "additional argument required\n");
exit(1);
}
}
2017-09-30 14:43:32 +02:00
else if (*arg == 'Z')
fastkeygen = 0;
2017-09-24 21:13:16 +02:00
else if (*arg == 'z')
fastkeygen = 1;
2017-09-30 05:40:12 +02:00
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
}
2017-09-30 14:43:32 +02:00
else if (*arg == 'T') {
2017-09-30 05:46:17 +02:00
#ifdef STATISTICS
2017-09-30 14:43:32 +02:00
realtimestats = 0;
2017-09-30 05:46:17 +02:00
#else
fprintf(stderr,"statistics support not compiled in\n");
exit(1);
#endif
}
2017-09-24 21:13:16 +02:00
else {
fprintf(stderr, "unrecognised argument: -%c\n", *arg);
exit(1);
}
if (numargit)
goto nextarg;
}
else filters_add(arg);
}
2017-09-25 19:49:47 +02:00
2017-09-24 21:13:16 +02:00
if (!quietflag)
printfilters();
2017-09-25 22:57:27 +02:00
if (!filters_count())
return 0;
2017-09-30 05:40:12 +02:00
if (outfile)
fout = fopen(outfile, "w");
2017-09-24 21:13:16 +02:00
if (workdir)
mkdir(workdir, 0700);
2017-09-25 19:49:47 +02:00
2017-09-24 21:13:16 +02:00
direndpos = workdirlen;
onionendpos = workdirlen + ONIONLEN;
2017-09-25 19:49:47 +02:00
2017-09-24 21:13:16 +02:00
if (!dirnameflag) {
printstartpos = direndpos;
printlen = ONIONLEN + 1;
} else {
printstartpos = 0;
printlen = onionendpos + 1;
}
2017-09-25 19:49:47 +02:00
2017-09-24 21:13:16 +02:00
if (numthreads <= 0) {
2017-10-06 02:14:33 +02:00
numthreads = cpucount();
if (numthreads <= 0)
numthreads = 1;
2017-09-24 21:13:16 +02:00
}
2017-09-25 19:49:47 +02:00
2017-09-30 05:40:12 +02:00
signal(SIGTERM,termhandler);
signal(SIGINT,termhandler);
2017-09-25 19:49:47 +02:00
2017-09-24 21:13:16 +02:00
VEC_INIT(threads);
VEC_ADDN(threads,numthreads);
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
VEC_INIT(stats);
VEC_ADDN(stats,numthreads);
VEC_ZERO(stats);
2017-09-30 05:40:12 +02:00
VEC_INIT(tstats);
VEC_ADDN(tstats,numthreads);
VEC_ZERO(tstats);
2017-09-30 05:40:12 +02:00
#endif
2017-09-25 19:49:47 +02:00
2017-09-30 05:40:12 +02:00
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);
2017-09-24 21:13:16 +02:00
if (tret) {
2017-09-30 05:40:12 +02:00
fprintf(stderr,"error while making %dth thread: %d\n",(int)i,tret);
2017-09-24 21:13:16 +02:00
exit(1);
}
}
2017-09-25 19:49:47 +02:00
2017-09-30 05:40:12 +02:00
#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
2017-09-25 19:49:47 +02:00
struct timespec ts;
memset(&ts,0,sizeof(ts));
ts.tv_nsec = 100000000;
while (!endwork) {
if (numneedgenerate && keysgenerated >= numneedgenerate) {
endwork = 1;
break;
}
nanosleep(&ts,0);
2017-09-30 05:40:12 +02:00
#ifdef STATISTICS
clock_gettime(CLOCK_MONOTONIC,&nowtime);
inowtime = (1000000 * (u64)nowtime.tv_sec) + (nowtime.tv_nsec / 1000);
u64 sumcalc = 0,sumsuccess = 0;
for (size_t i = 0;i < numthreads;++i) {
u32 newt,tdiff;
// numcalc
2017-10-06 02:25:51 +02:00
newt = VEC_BUF(stats,i).numcalc.v;
2017-09-30 05:40:12 +02:00
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
2017-10-06 02:25:51 +02:00
newt = VEC_BUF(stats,i).numsuccess.v;
2017-09-30 05:40:12 +02:00
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;
}
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 successpersec = (1000000.0 * sumsuccess) / (inowtime - istarttime);
fprintf(stderr,">calc/sec:%8lf, success/sec:%8lf, elapsed:%5.6lfsec\n",calcpersec,successpersec,(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;
}
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;
}
u64 timediff = (inowtime - istarttime + 1) / 2;
elapsedoffset += timediff;
istarttime += timediff;
}
#endif
2017-09-25 19:49:47 +02:00
}
2017-09-30 05:40:12 +02:00
if (!quietflag)
fprintf(stderr, "waiting for threads to finish...\n");
for (size_t i = 0;i < VEC_LENGTH(threads);++i)
pthread_join(VEC_BUF(threads,i),0);
if (!quietflag)
fprintf(stderr, "done, quitting\n");
2017-09-24 21:13:16 +02:00
2017-09-25 19:49:47 +02:00
pthread_mutex_destroy(&keysgenerated_mutex);
2017-09-24 21:13:16 +02:00
pthread_mutex_destroy(&fout_mutex);
filters_clean();
2017-09-25 19:49:47 +02:00
if (outfile)
fclose(fout);
2017-09-24 21:13:16 +02:00
return 0;
}