From 6a416795b7afd03fe0d4fec69759faae3e300f49 Mon Sep 17 00:00:00 2001 From: heios Date: Fri, 6 Jul 2018 01:20:51 +0100 Subject: [PATCH 1/5] optional functionality for writing results to the single file and extracting specific keys from it --- Makefile.in | 16 ++- base64.h | 11 ++ base64_from.c | 90 +++++++++++++ base64_to.c | 51 ++++++++ constants.h | 41 ++++++ main.c | 348 +++++++++++++++++++++++++++++++++++++++++++------- test_base64.c | 44 +++++++ 7 files changed, 554 insertions(+), 47 deletions(-) create mode 100644 base64.h create mode 100644 base64_from.c create mode 100644 base64_to.c create mode 100644 constants.h create mode 100644 test_base64.c diff --git a/Makefile.in b/Makefile.in index 6f70dca..60f38a7 100644 --- a/Makefile.in +++ b/Makefile.in @@ -26,10 +26,17 @@ MAINOBJ= \ cpucount.c.o \ base32_to.c.o \ base32_from.c.o \ + base64_to.c.o \ + base64_from.c.o \ ioutil.c.o \ $(ED25519OBJ) \ keccak.c.o +TEST_BASE64OBJ= \ + test_base64.c.o \ + base64_to.c.o \ + base64_from.c.o + TEST_BASE32OBJ= \ test_base32.c.o \ base32_to.c.o \ @@ -48,6 +55,7 @@ TEST_ED25519OBJ= \ ALLO= $(sort \ $(MAINOBJ) \ + $(TEST_BASE64OBJ) \ $(TEST_BASE32OBJ) \ $(TEST_BASE16OBJ) \ $(TEST_ED25519OBJ) \ @@ -60,7 +68,7 @@ CLEANO= $(filter %.o,$(ALLO)) MAINLIB= -lpthread -lsodium @MAINLIB@ TEST_ED25519LIB= -lsodium -EXE= mkp224o test_base32 test_base16 test_ed25519 +EXE= mkp224o test_base64 test_base32 test_base16 test_ed25519 default: mkp224o @@ -69,6 +77,9 @@ all: $(EXE) mkp224o: $(MAINOBJ) $(CC) $(LDFLAGS) $(CFLAGS) -o $@.tmp $^ $(MAINLIB) && $(MV) $@.tmp $@ +test_base64: $(TEST_BASE64OBJ) + $(CC) $(LDFLAGS) $(CFLAGS) -o $@.tmp $^ && $(MV) $@.tmp $@ + test_base32: $(TEST_BASE32OBJ) $(CC) $(LDFLAGS) $(CFLAGS) -o $@.tmp $^ && $(MV) $@.tmp $@ @@ -104,6 +115,8 @@ base16_from.c.o: types.h base16.h base16_to.c.o: types.h base16.h base32_from.c.o: types.h base32.h base32_to.c.o: types.h base32.h +base64_from.c.o: types.h base64.h +base64_to.c.o: types.h base64.h cpucount.c.o: cpucount.h ed25519/amd64-51-30k/batch.c.o: ed25519/amd64-51-30k/crypto_sign.h ed25519/amd64-51-30k/batch.c.o: ed25519/amd64-51-30k/ed25519.h @@ -384,6 +397,7 @@ main.c.o: ed25519/ed25519-donna/ed25519-donna-64bit-x86.h main.c.o: ed25519/ed25519-donna/ed25519-donna-impl-base.h ioutil.h filters.h test_base16.c.o: types.h base16.h test_base32.c.o: types.h base32.h +test_base64.c.o: types.h base64.h test_ed25519.c.o: types.h base16.h ed25519/ed25519.h ed25519/ref10/ed25519.h test_ed25519.c.o: ed25519/ref10/ge.h ed25519/ref10/fe.h test_ed25519.c.o: ed25519/ref10/crypto_int32.h ed25519/amd64-51-30k/ed25519.h diff --git a/base64.h b/base64.h new file mode 100644 index 0000000..570de19 --- /dev/null +++ b/base64.h @@ -0,0 +1,11 @@ +// converts src[0:slen] to base64 string +char *base64_to(char *dst,const u8 *src,size_t slen); +// calculates length needed to store data converted to base64 +#define BASE64_TO_LEN(l) (((l + 3 - 1) / 3) * 4) +// converts src string from base64 +size_t base64_from(u8 *dst,const char *src,size_t slen); +// calculates length needed to store data converted from base +#define BASE64_FROM_LEN(l) ((l) / 4 * 3) +// validates base32 string and optionally stores length of valid data +// returns 1 if whole string is good, 0 if string contains invalid data +int base64_valid(const char *src,size_t *count); diff --git a/base64_from.c b/base64_from.c new file mode 100644 index 0000000..aabd84f --- /dev/null +++ b/base64_from.c @@ -0,0 +1,90 @@ +#include +#include +#include "types.h" +#include "base64.h" + +static const u8 base64f[256] = { + //00 01 02 03 04 05 06 07 + //08 09 0A 0B 0C 0D 0E 0F + // 0x00..0x3F + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x00 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x08 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x10 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x18 + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x20 + 0xFF, 0xFF, 0xFF, 0x3E, 0xFF, 0xFF, 0xFF, 0x3F, // 0x28 + 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x3B, // 0x30 + 0x3C, 0x3D, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x38 + // 0x40..0x7F + 0xFF, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, // 0x40 + 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, // 0x48 + 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, // 0x50 + 0x17, 0x18, 0x19, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x58 + 0xFF, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, // 0x60 + 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, // 0x68 + 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E, 0x2F, 0x30, // 0x70 + 0x31, 0x32, 0x33, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // 0x78 + // 0x80..0xBF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + // 0xC0..0xFF + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, + 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, +}; + +size_t base64_from(u8 *dst,const char *src,size_t srclen) +{ + if (srclen % 4) { + return -1; + } else if (!srclen) { + return 0; + } + + size_t dstlen = BASE64_FROM_LEN(srclen); + dstlen -= (src[srclen - 1] == '='); + dstlen -= (src[srclen - 2] == '='); + + for (size_t i = 0, j = 0; i < srclen;) { + u32 sixbits[4]; + + sixbits[0] = base64f[(unsigned char)src[i++]]; + sixbits[1] = base64f[(unsigned char)src[i++]]; + sixbits[2] = (src[i] == '=' ? (0 & i++) : base64f[(unsigned char)src[i++]]); + sixbits[3] = (src[i] == '=' ? (0 & i++) : base64f[(unsigned char)src[i++]]); + + u32 threebytes = 0 + | (sixbits[0] << (3 * 6)) + | (sixbits[1] << (2 * 6)) + | (sixbits[2] << (1 * 6)) + | (sixbits[3] << (0 * 6)); + + if (j < dstlen) dst[j++] = (threebytes >> (2 * 8)); + if (j < dstlen) dst[j++] = (threebytes >> (1 * 8)) & 0xff; + if (j < dstlen) dst[j++] = (threebytes >> (0 * 8)) & 0xff; + } + return dstlen; +} + +int base64_valid(const char *src,size_t *count) +{ + const char *p; + + for (p = src;base64f[(u8)*p] != 0xFF;++p) + ; + + if (count) + *count = (size_t) (p - src); + return !*p; +} diff --git a/base64_to.c b/base64_to.c new file mode 100644 index 0000000..a140148 --- /dev/null +++ b/base64_to.c @@ -0,0 +1,51 @@ +#include +#include +#include "types.h" +#include "base64.h" + +static const char base64t[64] = { + 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', + 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', + 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', + 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', + 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', + 'w', 'x', 'y', 'z', '0', '1', '2', '3', + '4', '5', '6', '7', '8', '9', '+', '/', +}; + +char *base64_to(char *dst,const u8 *src,size_t slen) +{ + if (!slen) { + *dst = '\0'; + return dst; + } + + for(size_t i = 0; i < slen;) { + u32 threebytes = 0; + threebytes |= (i < slen ? (unsigned char)src[i++] : (unsigned char)0) << (2 * 8); + threebytes |= (i < slen ? (unsigned char)src[i++] : (unsigned char)0) << (1 * 8); + threebytes |= (i < slen ? (unsigned char)src[i++] : (unsigned char)0) << (0 * 8); + + *dst++ = base64t[(threebytes >> (3 * 6)) & 63]; + *dst++ = base64t[(threebytes >> (2 * 6)) & 63]; + *dst++ = base64t[(threebytes >> (1 * 6)) & 63]; + *dst++ = base64t[(threebytes >> (0 * 6)) & 63]; + } + + switch (slen % 3) { + case 0 : break; + case 1 : { + *(dst-2) = '='; + *(dst-1) = '='; + break; + } + case 2 : { + *(dst-1) = '='; + break; + } + } + + *dst = '\0'; + return dst; +} diff --git a/constants.h b/constants.h new file mode 100644 index 0000000..308e5ae --- /dev/null +++ b/constants.h @@ -0,0 +1,41 @@ + +#define LINEFEED_LEN (sizeof(char)) +#define NULLTERM_LEN (sizeof(char)) +#define PATH_SEPARATOR_LEN (sizeof(char)) + +static const int use_secret_mode = 1; +static const int use_public_mode = 0; + +static const char hostname_filename[] = "hostname"; +static const char secret_key_filename[] = "hs_ed25519_secret_key"; +static const char public_key_filename[] = "hs_ed25519_public_key"; + +static const char keys_field_generated[] = "generated:"; +static const char keys_field_hostname[] = " - hostname: "; +static const char keys_field_secretkey[] = " - hs_ed25519_secret_key: "; +static const char keys_field_publickey[] = " - hs_ed25519_public_key: "; +static const char keys_field_time[] = " - time: "; + +#define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN) +#define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN) +#define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN) +#define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN) +#define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN) + +static const char hostname_example[] = "xxxxxvsjzke274nisktdqcl3eqm5ve3m6iur6vwme7m5p6kxivrvjnyd.onion"; +static const char seckey_example[] = "PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACwCPMr6rvBRtkW7ZzZ8P7Ne4acRZrhPrN/EF6AETRraFGvdrkW5es4WXB2UxrbuUf8zPoIKkXK5cpdakYdUeM3"; +static const char pubkey_example[] = "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC973vWScqJr/GokqY4CXskGdqTbPIpH1bMJ9nX+VdFYw=="; +static const char time_example[] = "2018-07-04 21:31:20"; + +#define HOSTNAME_LEN (sizeof(hostname_example) - NULLTERM_LEN) +#define SECKEY_LEN (sizeof(seckey_example) - NULLTERM_LEN) +#define PUBKEY_LEN (sizeof(pubkey_example) - NULLTERM_LEN) +#define TIME_LEN (sizeof(time_example) - NULLTERM_LEN) + +#define KEYS_LEN ( KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN \ + + KEYS_FIELD_HOSTNAME_LEN + HOSTNAME_LEN + LINEFEED_LEN \ + + KEYS_FIELD_SECRETKEY_LEN + SECKEY_LEN + LINEFEED_LEN \ + + KEYS_FIELD_PUBLICKEY_LEN + PUBKEY_LEN + LINEFEED_LEN \ + + KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \ + + LINEFEED_LEN \ +) diff --git a/main.c b/main.c index 4a5385f..59ea657 100644 --- a/main.c +++ b/main.c @@ -2,6 +2,7 @@ #define _POSIX_C_SOURCE 200112L #endif +#include #include #include #include @@ -16,10 +17,12 @@ #include "likely.h" #include "vec.h" #include "base32.h" +#include "base64.h" #include "cpucount.h" #include "keccak.h" #include "ed25519/ed25519.h" #include "ioutil.h" +#include "constants.h" #ifndef _WIN32 #define FSZ "%zu" @@ -28,12 +31,12 @@ #endif // 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 +static const char pkprefix[] = "== ed25519v1-public: type0 ==\0\0"; +#define pkprefixlen (sizeof(pkprefix)) // three null-terminators included +static const char skprefix[] = "== ed25519v1-secret: type0 ==\0\0"; +#define skprefixlen (sizeof(skprefix)) // three null-terminators included +static const char checksumstr[] = ".onion checksum"; +#define checksumstrlen (sizeof(checksumstr) - NULLTERM_LEN) // output directory static char *workdir = 0; @@ -49,7 +52,10 @@ static int quietflag = 0; // with checksum + version num #define PUBONION_LEN (PUBLIC_LEN + 3) // with newline included -#define ONIONLEN 62 +#define ONION_LEN 62 + +#define FORMATTED_SECRET_LEN (skprefixlen + SECRET_LEN) +#define FORMATTED_PUBLIC_LEN (pkprefixlen + PUBLIC_LEN) static size_t onionendpos; // end of .onion within string static size_t direndpos; // end of dir before .onion within string @@ -58,6 +64,7 @@ static size_t printlen; // precalculated, related to printstartpos static pthread_mutex_t fout_mutex; static FILE *fout; +const char *outfilekeys = 0; static size_t numneedgenerate = 0; static int numwords = 1; static pthread_mutex_t keysgenerated_mutex; @@ -111,6 +118,80 @@ struct tstatstruct { VEC_STRUCT(tstatsvec,struct tstatstruct); #endif +typedef union { + u8 raw[pkprefixlen + PUBLIC_LEN + 32]; + struct { + u64 prefix[4]; + u64 key[4]; + u64 hash[4]; + } i; +} pubonionunion; + + + +#define BUF_APPEND(buf,offset,src,srclen) strncpy(&buf[offset],src,srclen); offset += srclen; +#define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src)) +#define BUF_APPEND_CHAR(buf,offet,c) buf[offset++] = c; + +static void writekeys(const char *hostname, const u8 *formated_secret, const u8 *formated_public) +{ + char keysbuf[KEYS_LEN]; + size_t offset = 0; + + BUF_APPEND_CSTR(keysbuf, offset, keys_field_generated); + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + BUF_APPEND_CSTR(keysbuf, offset, keys_field_hostname); + BUF_APPEND(keysbuf, offset, hostname, ONION_LEN); + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + BUF_APPEND_CSTR(keysbuf, offset, keys_field_secretkey); + char seckeybuf[SECKEY_LEN + NULLTERM_LEN]; + base64_to(seckeybuf, formated_secret, FORMATTED_SECRET_LEN); + BUF_APPEND(keysbuf, offset, seckeybuf, SECKEY_LEN); + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + BUF_APPEND_CSTR(keysbuf, offset, keys_field_publickey); + char pubkeybuf[PUBKEY_LEN + NULLTERM_LEN]; + base64_to(pubkeybuf, formated_public, FORMATTED_PUBLIC_LEN); + BUF_APPEND(keysbuf, offset, pubkeybuf, PUBKEY_LEN); + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + BUF_APPEND_CSTR(keysbuf, offset, keys_field_time); + char timebuf[TIME_LEN + NULLTERM_LEN]; + time_t timer; + struct tm* tm_info; + time(&timer); + tm_info = localtime(&timer); + strftime(timebuf, TIME_LEN + NULLTERM_LEN, "%Y-%m-%d %H:%M:%S", tm_info); + BUF_APPEND(keysbuf, offset, timebuf, TIME_LEN); + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + BUF_APPEND_CHAR(keysbuf, offset, '\n'); + + assert(offset == KEYS_LEN); + + pthread_mutex_lock(&fout_mutex); + fwrite(keysbuf,sizeof(keysbuf),1,fout); + fflush(fout); + pthread_mutex_unlock(&fout_mutex); +} + +#undef BUF_APPEND_CHAR +#undef BUF_APPEND_CSTR +#undef BUF_APPEND + +static void printhostname(const char *hostname) +{ + char buf[ONION_LEN + LINEFEED_LEN]; + strncpy(buf,hostname,ONION_LEN); + buf[ONION_LEN] = '\n'; + + pthread_mutex_lock(&fout_mutex); + fwrite(buf,sizeof(buf),1,fout); + fflush(fout); + pthread_mutex_unlock(&fout_mutex); +} static void onionready(char *sname,const u8 *secret,const u8 *pubonion) { @@ -125,12 +206,6 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) } } - if (createdir(sname,1) != 0) { - if (numneedgenerate) - pthread_mutex_unlock(&keysgenerated_mutex); - return; - } - if (numneedgenerate) { ++keysgenerated; if (keysgenerated >= numneedgenerate) @@ -138,6 +213,21 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) pthread_mutex_unlock(&keysgenerated_mutex); } + if (fout) { + if (outfilekeys) { + writekeys(&sname[printstartpos],secret,pubonion); + return; + } else { + printhostname(&sname[printstartpos]); + } + } + + if (createdir(sname,1) != 0) { + if (numneedgenerate) + pthread_mutex_unlock(&keysgenerated_mutex); + return; + } + strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); writetofile(sname,secret,skprefixlen + SECRET_LEN,1); @@ -145,20 +235,13 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) FILE *hfile = fopen(sname,"w"); if (hfile) { sname[onionendpos] = '\n'; - fwrite(&sname[direndpos],ONIONLEN + 1,1,hfile); + fwrite(&sname[direndpos],ONION_LEN + 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 @@ -188,14 +271,7 @@ static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits) 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; + pubonionunion pubonion; u8 * const pk = &pubonion.raw[pkprefixlen]; u8 secret[skprefixlen + SECRET_LEN]; u8 * const sk = &secret[skprefixlen]; @@ -217,7 +293,7 @@ static void *dowork(void *task) memcpy(hashsrc,checksumstr,checksumstrlen); hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version - sname = (char *) malloc(workdirlen + ONIONLEN + 63 + 1); + sname = (char *) malloc(workdirlen + ONION_LEN + 63 + 1); if (!sname) abort(); if (workdir) @@ -295,14 +371,7 @@ static void addsztoscalar32(u8 *dst,size_t v) 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; + pubonionunion pubonion; u8 * const pk = &pubonion.raw[pkprefixlen]; u8 secret[skprefixlen + SECRET_LEN]; u8 * const sk = &secret[skprefixlen]; @@ -326,7 +395,7 @@ static void *dofastwork(void *task) memcpy(hashsrc,checksumstr,checksumstrlen); hashsrc[checksumstrlen + PUBLIC_LEN] = 0x03; // version - sname = (char *) malloc(workdirlen + ONIONLEN + 63 + 1); + sname = (char *) malloc(workdirlen + ONION_LEN + 63 + 1); if (!sname) abort(); if (workdir) @@ -338,13 +407,13 @@ initseed: #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; @@ -411,6 +480,8 @@ static void printhelp(FILE *out,const char *progname) "\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-O filename - output onion names with base64 encoded keys to specified file\n" + "\t-i filename host.onion - read file with keys and create directory with keys for specified host\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" @@ -470,7 +541,7 @@ static void setworkdir(const char *wd) if (needslash) s[l++] = '/'; s[l] = 0; - + workdir = s; workdirlen = l; if (!quietflag) @@ -479,6 +550,164 @@ static void setworkdir(const char *wd) VEC_STRUCT(threadvec, pthread_t); + +int parseandcreate(const char *filepath, const char *hostname) +{ + if (strlen(hostname) != ONION_LEN) { + fprintf(stderr, "Invalid onion address \"%s\".\n", hostname); + return 1; + } + + char buf[16*1024]; + memset(buf, 0, sizeof(buf)); + FILE *fkeys = fopen(filepath, "r"); + if (fkeys == NULL) { + fprintf(stderr, "Cannot open file with keys \"%s\" for reading.\n", filepath); + return 2; + } + int error_number = 1; + size_t readbytes = 0; + while (1) { + const size_t currentread = fread( + buf + readbytes, // Possibly we already partially receive desired onion address + sizeof(buf[0]), + sizeof(buf) - readbytes - NULLTERM_LEN, + fkeys); + if (currentread == 0) { + fprintf(stderr, "Not found desired hostname \"%s\" in file \"%s\".\n", hostname, filepath); + error_number = 3; + break; + } + readbytes += currentread; + buf[readbytes] = '\0'; + char *pfound = strstr(buf, hostname); + if (pfound == NULL) { + if (readbytes > ONION_LEN) { + memmove(buf, buf + readbytes - ONION_LEN, ONION_LEN); + readbytes = ONION_LEN; + } + } else { // Got it! + memmove(buf, pfound, readbytes - (pfound - buf)); + readbytes -= pfound - buf; + buf[readbytes] = '\0'; + char *pendrecord = NULL; + while (1) { + const size_t currentread = fread( + buf + readbytes, + sizeof(buf[0]), + sizeof(buf) - readbytes - NULLTERM_LEN, + fkeys); + readbytes += currentread; + buf[readbytes] = '\0'; + pendrecord = strstr(buf, "\n\n"); + if (pendrecord != NULL || currentread == 0) { + break; + } + } + if (pendrecord == NULL) { + fprintf(stderr, "Looks like file with keys \"%s\" is incomplete, found hostname but not keys.\n", filepath); + error_number = 4; + break; + } + + const char *const pfield_sec_begin = strstr(buf, keys_field_secretkey); + if (pfield_sec_begin == NULL) { + fprintf(stderr, "Cannot find field with secret key within generated section.\n"); + error_number = 5; + break; + } + const char *const p_sec_begin = pfield_sec_begin + KEYS_FIELD_SECRETKEY_LEN; + if (pendrecord - p_sec_begin < BASE64_TO_LEN(FORMATTED_SECRET_LEN)) { + fprintf(stderr, "Generated section it too small to keep base64 encoding of secret key.\n"); + error_number = 6; + break; + } + char secbuf[FORMATTED_SECRET_LEN]; + if (-1 == base64_from((u8*)secbuf, p_sec_begin, BASE64_TO_LEN(FORMATTED_SECRET_LEN))) { + fprintf(stderr, "Invalid base64 encoding of secret key.\n"); + error_number = 7; + break; + } + + const char *const pfield_pub_begin = strstr(buf, keys_field_publickey); + if (pfield_pub_begin == NULL) { + fprintf(stderr, "Cannot find field with public key within generated section.\n"); + error_number = 8; + break; + } + const char *const p_pub_begin = pfield_pub_begin + KEYS_FIELD_PUBLICKEY_LEN; + if (pendrecord - p_pub_begin < BASE64_TO_LEN(KEYS_FIELD_PUBLICKEY_LEN)) { + fprintf(stderr, "Generated section it too small to keep base64 encoding of public key.\n"); + error_number = 9; + break; + } + char pubbuf[FORMATTED_PUBLIC_LEN]; + if (-1 == base64_from((u8*)pubbuf, p_pub_begin, BASE64_TO_LEN(FORMATTED_PUBLIC_LEN))) { + fprintf(stderr, "Invalid base64 encoding of secret key.\n"); + error_number = 10; + break; + } + + char pathbuf[1024]; + const size_t keys_directory_path_len = workdirlen + strlen(hostname); + if (keys_directory_path_len >= sizeof(pathbuf)) { + fprintf(stderr, "Keys directory path to is too long: %ld, max allowed length is %ld.\n", keys_directory_path_len, sizeof(pathbuf)); + error_number = 11; + break; + } + strncpy(pathbuf, workdir, workdirlen); + strncpy(pathbuf + workdirlen, hostname, strlen(hostname)); + pathbuf[keys_directory_path_len] = '\0'; + if (-1 == createdir(pathbuf, use_secret_mode)) { + fprintf(stderr, "Cannot create directory \"%s\" for key files.\n", pathbuf); + error_number = 12; + break; + } + + const size_t secretkey_filepath_len = keys_directory_path_len + PATH_SEPARATOR_LEN + strlen(secret_key_filename); + if (secretkey_filepath_len >= sizeof(pathbuf)) { + fprintf(stderr, "Path to file with secret key is too long %ld, max allowed length is %ld.\n", secretkey_filepath_len, sizeof(pathbuf)); + error_number = 13; + break; + } + pathbuf[keys_directory_path_len] = '/'; + strncpy(pathbuf + keys_directory_path_len + PATH_SEPARATOR_LEN, secret_key_filename, strlen(secret_key_filename)); + pathbuf[secretkey_filepath_len] = '\0'; + if (-1 == writetofile(pathbuf, (u8*)secbuf, sizeof(secbuf), use_secret_mode)) { + fprintf(stderr, "Can't write secret key to file \"%s\".\n", pathbuf); + error_number = 14; + break; + } + + const size_t publickey_filepath_len = keys_directory_path_len + PATH_SEPARATOR_LEN + strlen(public_key_filename); + if (publickey_filepath_len >= sizeof(pathbuf)) { + fprintf(stderr, "Path to file with public key is too long %ld, max allowed length is %ld.\n", publickey_filepath_len, sizeof(pathbuf)); + error_number = 15; + break; + } + pathbuf[keys_directory_path_len] = '/'; + strncpy(pathbuf + keys_directory_path_len + PATH_SEPARATOR_LEN, public_key_filename, strlen(public_key_filename)); + pathbuf[publickey_filepath_len] = '\0'; + if (-1 == writetofile(pathbuf, (u8*)pubbuf, sizeof(pubbuf), use_public_mode)) { + fprintf(stderr, "Can't write public key to file \"%s\".\n", pathbuf); + error_number = 16; + break; + } + + pathbuf[keys_directory_path_len] = '\0'; + fprintf(stderr, "Keys successfully exported to directory \"%s\".\n", pathbuf); + error_number = 0; + break; + } + } + + if (ferror(fkeys) || error_number) { + fprintf(stderr, "Error #%d while parsing generated file \"%s\" or extracting keys.\n", error_number, filepath); + } + fclose(fkeys); + return error_number; +} + int main(int argc,char **argv) { const char *outfile = 0; @@ -561,6 +790,27 @@ int main(int argc,char **argv) else e_additional(); } + else if (*arg == 'O') { + if (argc--) + outfilekeys = *argv++; + else + e_additional(); + } + else if (*arg == 'i') { + char * filepath = NULL; + char * hostname = NULL; + if (argc--) { + filepath = *argv++; + if (argc--) { + hostname = *argv++; + return parseandcreate(filepath, hostname); + } + else + e_additional(); + } + else + e_additional(); + } else if (*arg == 'F') dirnameflag = 1; else if (*arg == 'd') { @@ -626,7 +876,13 @@ int main(int argc,char **argv) filters_add(arg); } - if (outfile) { + if (outfilekeys) { + fout = fopen(outfilekeys,"a"); + if (!fout) { + perror("failed to open output file for keys"); + exit(Q_FAILOPENOUTPUT); + } + } else if (outfile) { fout = fopen(outfile,"w"); if (!fout) { perror("failed to open output file"); @@ -654,11 +910,11 @@ int main(int argc,char **argv) createdir(workdir,1); direndpos = workdirlen; - onionendpos = workdirlen + ONIONLEN; + onionendpos = workdirlen + ONION_LEN; if (!dirnameflag) { printstartpos = direndpos; - printlen = ONIONLEN + 1; + printlen = ONION_LEN + 1; } else { printstartpos = 0; printlen = onionendpos + 1; diff --git a/test_base64.c b/test_base64.c new file mode 100644 index 0000000..cebb030 --- /dev/null +++ b/test_base64.c @@ -0,0 +1,44 @@ +#include +#include +#include "types.h" +#include "base64.h" +#include +#include +#include +#include + +struct texttestcase { + const char *in; + const char *out; + const char *rev; +} tests0[] = { + {"", "", ""}, + {"f", "Zg==", "f"}, + {"fo", "Zm8=", "fo"}, + {"foo", "Zm9v", "foo"}, + {"foob", "Zm9vYg==", "foob"}, + {"fooba", "Zm9vYmE=", "fooba"}, + {"foobar", "Zm9vYmFy", "foobar"}, +}; + +int main(void) +{ + char buf[1024], buf2[1024]; + size_t r; + for (size_t i = 0; i < sizeof(tests0)/sizeof(tests0[0]); ++i) { + base64_to(buf, (const u8 *)tests0[i].in, strlen(tests0[i].in)); + if (strcmp(buf, tests0[i].out) != 0) { + printf("invalid encoding result: \"%s\" -> encoded as \"%s\", but expected \"%s\".\n", + tests0[i].in, buf, tests0[i].out); + return 1; + } + r = base64_from((u8 *)buf2, buf, strlen(buf)); + buf2[r] = '\0'; + if (strcmp(buf2, tests0[i].rev) != 0) { + printf("invalid decoding result: encoded \"%s\", decoded as \"%s\", but expected \"%s\".\n", + tests0[i].out, buf2, tests0[i].rev); + return 2; + } + } + return 0; +} From 4bdeb62f44d70a0e55496cdbbcabd916d80702be Mon Sep 17 00:00:00 2001 From: cathugger Date: Mon, 9 Jul 2018 11:42:58 +0000 Subject: [PATCH 2/5] -O -> -y, -i -> -Y; -o appends by default; introduce -O option to overwrite --- main.c | 57 ++++++++++++++++++++++++++++----------------------------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/main.c b/main.c index 59ea657..3fa4162 100644 --- a/main.c +++ b/main.c @@ -64,7 +64,7 @@ static size_t printlen; // precalculated, related to printstartpos static pthread_mutex_t fout_mutex; static FILE *fout; -const char *outfilekeys = 0; +static int yamloutput = 0; static size_t numneedgenerate = 0; static int numwords = 1; static pthread_mutex_t keysgenerated_mutex; @@ -214,7 +214,7 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) } if (fout) { - if (outfilekeys) { + if (yamloutput) { writekeys(&sname[printstartpos],secret,pubonion); return; } else { @@ -479,9 +479,8 @@ static void printhelp(FILE *out,const char *progname) "\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-O filename - output onion names with base64 encoded keys to specified file\n" - "\t-i filename host.onion - read file with keys and create directory with keys for specified host\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 (default - auto)\n" @@ -493,6 +492,8 @@ static void printhelp(FILE *out,const char *progname) "\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 key file and extract key(s) to filesystem\n" ,progname,progname); fflush(out); } @@ -716,6 +717,7 @@ int main(int argc,char **argv) int dirnameflag = 0; int numthreads = 0; int fastkeygen = 1; + int outfileoverwrite; struct threadvec threads; #ifdef STATISTICS struct statsvec stats; @@ -785,29 +787,16 @@ int main(int argc,char **argv) else if (*arg == 'x') fout = 0; else if (*arg == 'o') { + outfileoverwrite = 0; if (argc--) outfile = *argv++; else e_additional(); } else if (*arg == 'O') { + outfileoverwrite = 1; if (argc--) - outfilekeys = *argv++; - else - e_additional(); - } - else if (*arg == 'i') { - char * filepath = NULL; - char * hostname = NULL; - if (argc--) { - filepath = *argv++; - if (argc--) { - hostname = *argv++; - return parseandcreate(filepath, hostname); - } - else - e_additional(); - } + outfile = *argv++; else e_additional(); } @@ -865,6 +854,22 @@ int main(int argc,char **argv) e_nostatistics(); #endif } + else if (*arg == 'y') + yamloutput = 1; + else if (*arg == 'Y') { + const char *filepath = 0, *hostname = 0; + if (argc--) { + filepath = *argv++; + if (argc--) { + hostname = *argv++; + return parseandcreate(filepath, hostname); + } + else + e_additional(); + } + else + e_additional(); + } else { fprintf(stderr,"unrecognised argument: -%c\n",*arg); exit(Q_UNRECOGNISED); @@ -876,14 +881,8 @@ int main(int argc,char **argv) filters_add(arg); } - if (outfilekeys) { - fout = fopen(outfilekeys,"a"); - if (!fout) { - perror("failed to open output file for keys"); - exit(Q_FAILOPENOUTPUT); - } - } else if (outfile) { - fout = fopen(outfile,"w"); + if (outfile) { + fout = fopen(outfile,!outfileoverwrite ? "a" : "w"); if (!fout) { perror("failed to open output file"); exit(Q_FAILOPENOUTPUT); From be50d6b929316311035c47e574e59c5de594e7a7 Mon Sep 17 00:00:00 2001 From: cathugger Date: Mon, 9 Jul 2018 16:38:41 +0000 Subject: [PATCH 3/5] refactor,fix,rewrite parts,tweak --- Makefile.in | 5 +- common.h | 22 +++ constants.h | 41 ----- main.c | 454 +++++++++++++++------------------------------------- yaml.c | 269 +++++++++++++++++++++++++++++++ yaml.h | 4 + 6 files changed, 432 insertions(+), 363 deletions(-) create mode 100644 common.h delete mode 100644 constants.h create mode 100644 yaml.c create mode 100644 yaml.h diff --git a/Makefile.in b/Makefile.in index 60f38a7..42b1a50 100644 --- a/Makefile.in +++ b/Makefile.in @@ -22,6 +22,7 @@ ED25519OBJ= $(ED25519_@ED25519IMPL@) MAINOBJ= \ main.c.o \ + yaml.c.o \ vec.c.o \ cpucount.c.o \ base32_to.c.o \ @@ -394,7 +395,8 @@ main.c.o: ed25519/ed25519-donna/modm-donna-64bit.h main.c.o: ed25519/ed25519-donna/ed25519-donna-basepoint-table.h main.c.o: ed25519/ed25519-donna/ed25519-donna-64bit-tables.h main.c.o: ed25519/ed25519-donna/ed25519-donna-64bit-x86.h -main.c.o: ed25519/ed25519-donna/ed25519-donna-impl-base.h ioutil.h filters.h +main.c.o: ed25519/ed25519-donna/ed25519-donna-impl-base.h ioutil.h common.h +main.c.o: yaml.h filters.h test_base16.c.o: types.h base16.h test_base32.c.o: types.h base32.h test_base64.c.o: types.h base64.h @@ -417,3 +419,4 @@ test_ed25519.c.o: ed25519/ed25519-donna/ed25519-donna-64bit-tables.h test_ed25519.c.o: ed25519/ed25519-donna/ed25519-donna-64bit-x86.h test_ed25519.c.o: ed25519/ed25519-donna/ed25519-donna-impl-base.h vec.c.o: vec.h +yaml.c.o: types.h yaml.h ioutil.h base64.h common.h diff --git a/common.h b/common.h new file mode 100644 index 0000000..23ea257 --- /dev/null +++ b/common.h @@ -0,0 +1,22 @@ +#define SECRET_LEN 64 +#define PUBLIC_LEN 32 +#define SEED_LEN 32 +// with checksum + version num +#define PUBONION_LEN (PUBLIC_LEN + 3) + +#define PKPREFIX_SIZE (29 + 3) +#define SKPREFIX_SIZE (29 + 3) + +#define FORMATTED_PUBLIC_LEN (PKPREFIX_SIZE + PUBLIC_LEN) +#define FORMATTED_SECRET_LEN (SKPREFIX_SIZE + SECRET_LEN) + +// full onion address, WITHOUT newline +#define ONION_LEN 62 + +extern pthread_mutex_t fout_mutex; +extern FILE *fout; + +extern size_t onionendpos; // end of .onion within string +extern size_t direndpos; // end of dir before .onion within string +extern size_t printstartpos; // where to start printing from +extern size_t printlen; // precalculated, related to printstartpos diff --git a/constants.h b/constants.h deleted file mode 100644 index 308e5ae..0000000 --- a/constants.h +++ /dev/null @@ -1,41 +0,0 @@ - -#define LINEFEED_LEN (sizeof(char)) -#define NULLTERM_LEN (sizeof(char)) -#define PATH_SEPARATOR_LEN (sizeof(char)) - -static const int use_secret_mode = 1; -static const int use_public_mode = 0; - -static const char hostname_filename[] = "hostname"; -static const char secret_key_filename[] = "hs_ed25519_secret_key"; -static const char public_key_filename[] = "hs_ed25519_public_key"; - -static const char keys_field_generated[] = "generated:"; -static const char keys_field_hostname[] = " - hostname: "; -static const char keys_field_secretkey[] = " - hs_ed25519_secret_key: "; -static const char keys_field_publickey[] = " - hs_ed25519_public_key: "; -static const char keys_field_time[] = " - time: "; - -#define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN) -#define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN) -#define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN) -#define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN) -#define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN) - -static const char hostname_example[] = "xxxxxvsjzke274nisktdqcl3eqm5ve3m6iur6vwme7m5p6kxivrvjnyd.onion"; -static const char seckey_example[] = "PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACwCPMr6rvBRtkW7ZzZ8P7Ne4acRZrhPrN/EF6AETRraFGvdrkW5es4WXB2UxrbuUf8zPoIKkXK5cpdakYdUeM3"; -static const char pubkey_example[] = "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC973vWScqJr/GokqY4CXskGdqTbPIpH1bMJ9nX+VdFYw=="; -static const char time_example[] = "2018-07-04 21:31:20"; - -#define HOSTNAME_LEN (sizeof(hostname_example) - NULLTERM_LEN) -#define SECKEY_LEN (sizeof(seckey_example) - NULLTERM_LEN) -#define PUBKEY_LEN (sizeof(pubkey_example) - NULLTERM_LEN) -#define TIME_LEN (sizeof(time_example) - NULLTERM_LEN) - -#define KEYS_LEN ( KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN \ - + KEYS_FIELD_HOSTNAME_LEN + HOSTNAME_LEN + LINEFEED_LEN \ - + KEYS_FIELD_SECRETKEY_LEN + SECKEY_LEN + LINEFEED_LEN \ - + KEYS_FIELD_PUBLICKEY_LEN + PUBKEY_LEN + LINEFEED_LEN \ - + KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \ - + LINEFEED_LEN \ -) diff --git a/main.c b/main.c index 3fa4162..80e3df3 100644 --- a/main.c +++ b/main.c @@ -2,12 +2,10 @@ #define _POSIX_C_SOURCE 200112L #endif -#include #include #include #include #include -#include #include #include #include @@ -17,12 +15,12 @@ #include "likely.h" #include "vec.h" #include "base32.h" -#include "base64.h" #include "cpucount.h" #include "keccak.h" #include "ed25519/ed25519.h" #include "ioutil.h" -#include "constants.h" +#include "common.h" +#include "yaml.h" #ifndef _WIN32 #define FSZ "%zu" @@ -31,12 +29,11 @@ #endif // additional 0 terminator is added by C -static const char pkprefix[] = "== ed25519v1-public: type0 ==\0\0"; -#define pkprefixlen (sizeof(pkprefix)) // three null-terminators included -static const char skprefix[] = "== ed25519v1-secret: type0 ==\0\0"; -#define skprefixlen (sizeof(skprefix)) // three null-terminators included +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) - NULLTERM_LEN) +#define checksumstrlen (sizeof(checksumstr) - 1) // 15 // output directory static char *workdir = 0; @@ -46,31 +43,25 @@ static int quietflag = 0; //static int wantdedup = 0; #define wantdedup 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 ONION_LEN 62 +// 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 -#define FORMATTED_SECRET_LEN (skprefixlen + SECRET_LEN) -#define FORMATTED_PUBLIC_LEN (pkprefixlen + PUBLIC_LEN) - -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 int yamloutput = 0; -static size_t numneedgenerate = 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; +pthread_mutex_t fout_mutex; +FILE *fout; + static void termhandler(int sig) { switch (sig) { @@ -118,81 +109,6 @@ struct tstatstruct { VEC_STRUCT(tstatsvec,struct tstatstruct); #endif -typedef union { - u8 raw[pkprefixlen + PUBLIC_LEN + 32]; - struct { - u64 prefix[4]; - u64 key[4]; - u64 hash[4]; - } i; -} pubonionunion; - - - -#define BUF_APPEND(buf,offset,src,srclen) strncpy(&buf[offset],src,srclen); offset += srclen; -#define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src)) -#define BUF_APPEND_CHAR(buf,offet,c) buf[offset++] = c; - -static void writekeys(const char *hostname, const u8 *formated_secret, const u8 *formated_public) -{ - char keysbuf[KEYS_LEN]; - size_t offset = 0; - - BUF_APPEND_CSTR(keysbuf, offset, keys_field_generated); - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - BUF_APPEND_CSTR(keysbuf, offset, keys_field_hostname); - BUF_APPEND(keysbuf, offset, hostname, ONION_LEN); - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - BUF_APPEND_CSTR(keysbuf, offset, keys_field_secretkey); - char seckeybuf[SECKEY_LEN + NULLTERM_LEN]; - base64_to(seckeybuf, formated_secret, FORMATTED_SECRET_LEN); - BUF_APPEND(keysbuf, offset, seckeybuf, SECKEY_LEN); - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - BUF_APPEND_CSTR(keysbuf, offset, keys_field_publickey); - char pubkeybuf[PUBKEY_LEN + NULLTERM_LEN]; - base64_to(pubkeybuf, formated_public, FORMATTED_PUBLIC_LEN); - BUF_APPEND(keysbuf, offset, pubkeybuf, PUBKEY_LEN); - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - BUF_APPEND_CSTR(keysbuf, offset, keys_field_time); - char timebuf[TIME_LEN + NULLTERM_LEN]; - time_t timer; - struct tm* tm_info; - time(&timer); - tm_info = localtime(&timer); - strftime(timebuf, TIME_LEN + NULLTERM_LEN, "%Y-%m-%d %H:%M:%S", tm_info); - BUF_APPEND(keysbuf, offset, timebuf, TIME_LEN); - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - BUF_APPEND_CHAR(keysbuf, offset, '\n'); - - assert(offset == KEYS_LEN); - - pthread_mutex_lock(&fout_mutex); - fwrite(keysbuf,sizeof(keysbuf),1,fout); - fflush(fout); - pthread_mutex_unlock(&fout_mutex); -} - -#undef BUF_APPEND_CHAR -#undef BUF_APPEND_CSTR -#undef BUF_APPEND - -static void printhostname(const char *hostname) -{ - char buf[ONION_LEN + LINEFEED_LEN]; - strncpy(buf,hostname,ONION_LEN); - buf[ONION_LEN] = '\n'; - - pthread_mutex_lock(&fout_mutex); - fwrite(buf,sizeof(buf),1,fout); - fflush(fout); - pthread_mutex_unlock(&fout_mutex); -} - static void onionready(char *sname,const u8 *secret,const u8 *pubonion) { if (endwork) @@ -204,44 +120,60 @@ static void onionready(char *sname,const u8 *secret,const u8 *pubonion) pthread_mutex_unlock(&keysgenerated_mutex); return; } - } - - if (numneedgenerate) { ++keysgenerated; - if (keysgenerated >= numneedgenerate) + if (keysgenerated == numneedgenerate) endwork = 1; pthread_mutex_unlock(&keysgenerated_mutex); } - if (fout) { - if (yamloutput) { - writekeys(&sname[printstartpos],secret,pubonion); + 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; - } else { - printhostname(&sname[printstartpos]); } - } - if (createdir(sname,1) != 0) { - if (numneedgenerate) - pthread_mutex_unlock(&keysgenerated_mutex); - return; - } + strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); + writetofile(sname,secret,FORMATTED_SECRET_LEN,1); - strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); - writetofile(sname,secret,skprefixlen + 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"); - if (hfile) { + strcpy(&sname[onionendpos],"/hostname"); + FILE *hfile = fopen(sname,"w"); sname[onionendpos] = '\n'; - fwrite(&sname[direndpos],ONION_LEN + 1,1,hfile); - fclose(hfile); - } + 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); +} - strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); - writetofile(sname,pubonion,pkprefixlen + PUBLIC_LEN,0); +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 @@ -271,10 +203,10 @@ static inline void shiftpk(u8 *dst,const u8 *src,size_t sbits) static void *dowork(void *task) { - pubonionunion pubonion; - u8 * const pk = &pubonion.raw[pkprefixlen]; - u8 secret[skprefixlen + SECRET_LEN]; - u8 * const sk = &secret[skprefixlen]; + 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]; @@ -285,19 +217,15 @@ static void *dowork(void *task) #endif PREFILTER - memcpy(secret,skprefix,skprefixlen); + memcpy(secret,skprefix,SKPREFIX_SIZE); wpk[PUBLIC_LEN] = 0; memset(&pubonion,0,sizeof(pubonion)); - memcpy(pubonion.raw,pkprefix,pkprefixlen); + 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 = (char *) malloc(workdirlen + ONION_LEN + 63 + 1); - if (!sname) - abort(); - if (workdir) - memcpy(sname,workdir,workdirlen); + sname = makesname(); initseed: randombytes(seed,sizeof(seed)); @@ -371,10 +299,10 @@ static void addsztoscalar32(u8 *dst,size_t v) static void *dofastwork(void *task) { - pubonionunion pubonion; - u8 * const pk = &pubonion.raw[pkprefixlen]; - u8 secret[skprefixlen + SECRET_LEN]; - u8 * const sk = &secret[skprefixlen]; + 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]; @@ -387,19 +315,15 @@ static void *dofastwork(void *task) #endif PREFILTER - memcpy(secret,skprefix,skprefixlen); + memcpy(secret,skprefix,SKPREFIX_SIZE); wpk[PUBLIC_LEN] = 0; memset(&pubonion,0,sizeof(pubonion)); - memcpy(pubonion.raw,pkprefix,pkprefixlen); + 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 = (char *) malloc(workdirlen + ONION_LEN + 63 + 1); - if (!sname) - abort(); - if (workdir) - memcpy(sname,workdir,workdirlen); + sname = makesname(); initseed: #ifdef STATISTICS @@ -505,6 +429,7 @@ enum { Q_FAILOPENOUTPUT, Q_FAILTHREAD, Q_FAILTIME, + Q_FAILOPENINPUT, } ; static void e_additional() @@ -551,173 +476,18 @@ static void setworkdir(const char *wd) VEC_STRUCT(threadvec, pthread_t); - -int parseandcreate(const char *filepath, const char *hostname) -{ - if (strlen(hostname) != ONION_LEN) { - fprintf(stderr, "Invalid onion address \"%s\".\n", hostname); - return 1; - } - - char buf[16*1024]; - memset(buf, 0, sizeof(buf)); - FILE *fkeys = fopen(filepath, "r"); - if (fkeys == NULL) { - fprintf(stderr, "Cannot open file with keys \"%s\" for reading.\n", filepath); - return 2; - } - int error_number = 1; - size_t readbytes = 0; - while (1) { - const size_t currentread = fread( - buf + readbytes, // Possibly we already partially receive desired onion address - sizeof(buf[0]), - sizeof(buf) - readbytes - NULLTERM_LEN, - fkeys); - if (currentread == 0) { - fprintf(stderr, "Not found desired hostname \"%s\" in file \"%s\".\n", hostname, filepath); - error_number = 3; - break; - } - readbytes += currentread; - buf[readbytes] = '\0'; - char *pfound = strstr(buf, hostname); - if (pfound == NULL) { - if (readbytes > ONION_LEN) { - memmove(buf, buf + readbytes - ONION_LEN, ONION_LEN); - readbytes = ONION_LEN; - } - } else { // Got it! - memmove(buf, pfound, readbytes - (pfound - buf)); - readbytes -= pfound - buf; - buf[readbytes] = '\0'; - char *pendrecord = NULL; - while (1) { - const size_t currentread = fread( - buf + readbytes, - sizeof(buf[0]), - sizeof(buf) - readbytes - NULLTERM_LEN, - fkeys); - readbytes += currentread; - buf[readbytes] = '\0'; - pendrecord = strstr(buf, "\n\n"); - if (pendrecord != NULL || currentread == 0) { - break; - } - } - if (pendrecord == NULL) { - fprintf(stderr, "Looks like file with keys \"%s\" is incomplete, found hostname but not keys.\n", filepath); - error_number = 4; - break; - } - - const char *const pfield_sec_begin = strstr(buf, keys_field_secretkey); - if (pfield_sec_begin == NULL) { - fprintf(stderr, "Cannot find field with secret key within generated section.\n"); - error_number = 5; - break; - } - const char *const p_sec_begin = pfield_sec_begin + KEYS_FIELD_SECRETKEY_LEN; - if (pendrecord - p_sec_begin < BASE64_TO_LEN(FORMATTED_SECRET_LEN)) { - fprintf(stderr, "Generated section it too small to keep base64 encoding of secret key.\n"); - error_number = 6; - break; - } - char secbuf[FORMATTED_SECRET_LEN]; - if (-1 == base64_from((u8*)secbuf, p_sec_begin, BASE64_TO_LEN(FORMATTED_SECRET_LEN))) { - fprintf(stderr, "Invalid base64 encoding of secret key.\n"); - error_number = 7; - break; - } - - const char *const pfield_pub_begin = strstr(buf, keys_field_publickey); - if (pfield_pub_begin == NULL) { - fprintf(stderr, "Cannot find field with public key within generated section.\n"); - error_number = 8; - break; - } - const char *const p_pub_begin = pfield_pub_begin + KEYS_FIELD_PUBLICKEY_LEN; - if (pendrecord - p_pub_begin < BASE64_TO_LEN(KEYS_FIELD_PUBLICKEY_LEN)) { - fprintf(stderr, "Generated section it too small to keep base64 encoding of public key.\n"); - error_number = 9; - break; - } - char pubbuf[FORMATTED_PUBLIC_LEN]; - if (-1 == base64_from((u8*)pubbuf, p_pub_begin, BASE64_TO_LEN(FORMATTED_PUBLIC_LEN))) { - fprintf(stderr, "Invalid base64 encoding of secret key.\n"); - error_number = 10; - break; - } - - char pathbuf[1024]; - const size_t keys_directory_path_len = workdirlen + strlen(hostname); - if (keys_directory_path_len >= sizeof(pathbuf)) { - fprintf(stderr, "Keys directory path to is too long: %ld, max allowed length is %ld.\n", keys_directory_path_len, sizeof(pathbuf)); - error_number = 11; - break; - } - strncpy(pathbuf, workdir, workdirlen); - strncpy(pathbuf + workdirlen, hostname, strlen(hostname)); - pathbuf[keys_directory_path_len] = '\0'; - if (-1 == createdir(pathbuf, use_secret_mode)) { - fprintf(stderr, "Cannot create directory \"%s\" for key files.\n", pathbuf); - error_number = 12; - break; - } - - const size_t secretkey_filepath_len = keys_directory_path_len + PATH_SEPARATOR_LEN + strlen(secret_key_filename); - if (secretkey_filepath_len >= sizeof(pathbuf)) { - fprintf(stderr, "Path to file with secret key is too long %ld, max allowed length is %ld.\n", secretkey_filepath_len, sizeof(pathbuf)); - error_number = 13; - break; - } - pathbuf[keys_directory_path_len] = '/'; - strncpy(pathbuf + keys_directory_path_len + PATH_SEPARATOR_LEN, secret_key_filename, strlen(secret_key_filename)); - pathbuf[secretkey_filepath_len] = '\0'; - if (-1 == writetofile(pathbuf, (u8*)secbuf, sizeof(secbuf), use_secret_mode)) { - fprintf(stderr, "Can't write secret key to file \"%s\".\n", pathbuf); - error_number = 14; - break; - } - - const size_t publickey_filepath_len = keys_directory_path_len + PATH_SEPARATOR_LEN + strlen(public_key_filename); - if (publickey_filepath_len >= sizeof(pathbuf)) { - fprintf(stderr, "Path to file with public key is too long %ld, max allowed length is %ld.\n", publickey_filepath_len, sizeof(pathbuf)); - error_number = 15; - break; - } - pathbuf[keys_directory_path_len] = '/'; - strncpy(pathbuf + keys_directory_path_len + PATH_SEPARATOR_LEN, public_key_filename, strlen(public_key_filename)); - pathbuf[publickey_filepath_len] = '\0'; - if (-1 == writetofile(pathbuf, (u8*)pubbuf, sizeof(pubbuf), use_public_mode)) { - fprintf(stderr, "Can't write public key to file \"%s\".\n", pathbuf); - error_number = 16; - break; - } - - pathbuf[keys_directory_path_len] = '\0'; - fprintf(stderr, "Keys successfully exported to directory \"%s\".\n", pathbuf); - error_number = 0; - break; - } - } - - if (ferror(fkeys) || error_number) { - fprintf(stderr, "Error #%d while parsing generated file \"%s\" or extracting keys.\n", error_number, filepath); - } - fclose(fkeys); - return error_number; -} - 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 outfileoverwrite; + int yamlinput = 0; + int outfileoverwrite = 0; struct threadvec threads; #ifdef STATISTICS struct statsvec stats; @@ -732,8 +502,6 @@ int main(int argc,char **argv) setvbuf(stderr,0,_IONBF,0); fout = stdout; - pthread_mutex_init(&keysgenerated_mutex,0); - pthread_mutex_init(&fout_mutex,0); const char *progname = argv[0]; if (argc <= 1) { @@ -857,18 +625,23 @@ int main(int argc,char **argv) else if (*arg == 'y') yamloutput = 1; else if (*arg == 'Y') { - const char *filepath = 0, *hostname = 0; - if (argc--) { - filepath = *argv++; - if (argc--) { + yamlinput = 1; + if (argc) { + --argc; + infile = *argv++; + if (!*infile) + infile = 0; + if (argc) { + --argc; hostname = *argv++; - return parseandcreate(filepath, hostname); + if (!*hostname) + hostname = 0; + if (hostname && strlen(hostname) != ONION_LEN) { + fprintf(stderr,"bad onion argument length\n"); + exit(Q_UNRECOGNISED); + } } - else - e_additional(); } - else - e_additional(); } else { fprintf(stderr,"unrecognised argument: -%c\n",*arg); @@ -889,6 +662,11 @@ int main(int argc,char **argv) } } + if (!fout && yamloutput) { + fprintf(stderr,"nil output with yaml mode does not make sense\n"); + exit(Q_FAILOPENOUTPUT); // define new err code? + } + filters_prepare(); filters_print(); @@ -913,12 +691,41 @@ int main(int argc,char **argv) if (!dirnameflag) { printstartpos = direndpos; - printlen = ONION_LEN + 1; + printlen = ONION_LEN + 1; // + '\n' } else { printstartpos = 0; - printlen = onionendpos + 1; + 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 Q_FAILOPENINPUT; + } + } + tret = yamlin_parseandcreate(fin,sname,hostname); + if (infile) { + fclose(fin); + fin = 0; + } + free(sname); + + if (tret) + return tret; + + goto done; + } + + if (yamloutput) + yamlout_init(); + + pthread_mutex_init(&keysgenerated_mutex,0); + pthread_mutex_init(&fout_mutex,0); + if (numthreads <= 0) { numthreads = cpucount(); if (numthreads <= 0) @@ -1061,8 +868,13 @@ int main(int argc,char **argv) if (!quietflag) fprintf(stderr," done.\n"); + if (yamloutput) + yamlout_clean(); + pthread_mutex_destroy(&keysgenerated_mutex); pthread_mutex_destroy(&fout_mutex); + +done: filters_clean(); if (outfile) diff --git a/yaml.c b/yaml.c new file mode 100644 index 0000000..cdd418e --- /dev/null +++ b/yaml.c @@ -0,0 +1,269 @@ + +#include +#include +#include +#include +#include +#include +#include + +#include "types.h" +#include "yaml.h" +#include "ioutil.h" +#include "base64.h" +#include "common.h" + +#define LINEFEED_LEN (sizeof(char)) +#define NULLTERM_LEN (sizeof(char)) +#define PATH_SEPARATOR_LEN (sizeof(char)) + +static const char keys_field_generated[] = "---"; +static const char keys_field_hostname[] = "hostname: "; +static const char keys_field_publickey[] = "hs_ed25519_public_key: "; +static const char keys_field_secretkey[] = "hs_ed25519_secret_key: "; +static const char keys_field_time[] = "time: "; + +#define KEYS_FIELD_GENERATED_LEN (sizeof(keys_field_generated) - NULLTERM_LEN) +#define KEYS_FIELD_HOSTNAME_LEN (sizeof(keys_field_hostname) - NULLTERM_LEN) +#define KEYS_FIELD_PUBLICKEY_LEN (sizeof(keys_field_publickey) - NULLTERM_LEN) +#define KEYS_FIELD_SECRETKEY_LEN (sizeof(keys_field_secretkey) - NULLTERM_LEN) +#define KEYS_FIELD_TIME_LEN (sizeof(keys_field_time) - NULLTERM_LEN) + +static const char hostname_example[] = "xxxxxvsjzke274nisktdqcl3eqm5ve3m6iur6vwme7m5p6kxivrvjnyd.onion"; +static const char pubkey_example[] = "PT0gZWQyNTUxOXYxLXB1YmxpYzogdHlwZTAgPT0AAAC973vWScqJr/GokqY4CXskGdqTbPIpH1bMJ9nX+VdFYw=="; +static const char seckey_example[] = "PT0gZWQyNTUxOXYxLXNlY3JldDogdHlwZTAgPT0AAACwCPMr6rvBRtkW7ZzZ8P7Ne4acRZrhPrN/EF6AETRraFGvdrkW5es4WXB2UxrbuUf8zPoIKkXK5cpdakYdUeM3"; +static const char time_example[] = "2018-07-04 21:31:20 Z"; + +#define HOSTNAME_LEN (sizeof(hostname_example) - NULLTERM_LEN) +#define PUBKEY_LEN (sizeof(pubkey_example) - NULLTERM_LEN) +#define SECKEY_LEN (sizeof(seckey_example) - NULLTERM_LEN) +#define TIME_LEN (sizeof(time_example) - NULLTERM_LEN) + +#define KEYS_LEN ( \ + KEYS_FIELD_GENERATED_LEN + LINEFEED_LEN + \ + KEYS_FIELD_HOSTNAME_LEN + HOSTNAME_LEN + LINEFEED_LEN + \ + KEYS_FIELD_PUBLICKEY_LEN + PUBKEY_LEN + LINEFEED_LEN + \ + KEYS_FIELD_SECRETKEY_LEN + SECKEY_LEN + LINEFEED_LEN + \ + KEYS_FIELD_TIME_LEN + TIME_LEN + LINEFEED_LEN \ +) + +static pthread_mutex_t tminfo_mutex; + +void yamlout_init() +{ + pthread_mutex_init(&tminfo_mutex,0); +} + +void yamlout_clean() +{ + pthread_mutex_destroy(&tminfo_mutex); +} + +#define BUF_APPEND(buf,offset,src,srclen) \ +do { \ + memcpy(&buf[offset],(src),(srclen)); \ + offset += (srclen); \ +} while (0) +#define BUF_APPEND_CSTR(buf,offset,src) BUF_APPEND(buf,offset,src,strlen(src)) +#define BUF_APPEND_CHAR(buf,offset,c) buf[offset++] = (c) + +void yamlout_writekeys(const char *hostname,const u8 *formated_public,const u8 *formated_secret) +{ + char keysbuf[KEYS_LEN]; + char pubkeybuf[PUBKEY_LEN + NULLTERM_LEN]; + char seckeybuf[SECKEY_LEN + NULLTERM_LEN]; + char timebuf[TIME_LEN + NULLTERM_LEN]; + size_t offset = 0; + + BUF_APPEND(keysbuf,offset,keys_field_generated,KEYS_FIELD_GENERATED_LEN); + BUF_APPEND_CHAR(keysbuf,offset,'\n'); + + BUF_APPEND(keysbuf,offset,keys_field_hostname,KEYS_FIELD_HOSTNAME_LEN); + BUF_APPEND(keysbuf,offset,hostname,ONION_LEN); + BUF_APPEND_CHAR(keysbuf,offset,'\n'); + + BUF_APPEND(keysbuf,offset,keys_field_publickey,KEYS_FIELD_PUBLICKEY_LEN); + base64_to(pubkeybuf,formated_public,FORMATTED_PUBLIC_LEN); + BUF_APPEND(keysbuf,offset,pubkeybuf,PUBKEY_LEN); + BUF_APPEND_CHAR(keysbuf,offset,'\n'); + + BUF_APPEND(keysbuf,offset,keys_field_secretkey,KEYS_FIELD_SECRETKEY_LEN); + base64_to(seckeybuf,formated_secret,FORMATTED_SECRET_LEN); + BUF_APPEND(keysbuf,offset,seckeybuf,SECKEY_LEN); + BUF_APPEND_CHAR(keysbuf,offset,'\n'); + + BUF_APPEND(keysbuf,offset,keys_field_time,KEYS_FIELD_TIME_LEN); + + time_t currtime; + time(&currtime); + struct tm *tm_info; + + pthread_mutex_lock(&tminfo_mutex); + tm_info = gmtime(&currtime); + strftime(timebuf,sizeof(timebuf),"%Y-%m-%d %H:%M:%S Z",tm_info); + pthread_mutex_unlock(&tminfo_mutex); + + BUF_APPEND(keysbuf,offset,timebuf,TIME_LEN); + BUF_APPEND_CHAR(keysbuf,offset,'\n'); + + assert(offset == KEYS_LEN); + + pthread_mutex_lock(&fout_mutex); + fwrite(keysbuf,sizeof(keysbuf),1,fout); + fflush(fout); + pthread_mutex_unlock(&fout_mutex); +} + +#undef BUF_APPEND_CHAR +#undef BUF_APPEND_CSTR +#undef BUF_APPEND + +// pseudo YAML parser +int yamlin_parseandcreate(FILE *fin,char *sname,const char *hostname) +{ + char line[256]; + size_t len; + u8 pubbuf[FORMATTED_PUBLIC_LEN]; + u8 secbuf[FORMATTED_SECRET_LEN]; + int hashost = 0,haspub = 0,hassec = 0,skipthis = 0; + enum keytype { HOST, PUB, SEC } keyt; + + while (!feof(fin) && !ferror(fin)) { + if (!fgets(line,sizeof(line),fin)) + break; + + len = strlen(line); + + // trim whitespace from the end + while (len != 0 && (line[len-1] == ' ' || line[len-1] == '\n' || line[len-1] == '\r')) + line[--len] = '\0'; + + // skip empty lines + if (len == 0) + continue; + + if (len >= 3 && line[0] == '-' && line[1] == '-' && line[2] == '-') { + // end of document indicator + if (!skipthis && (hashost || haspub || hassec)) { + fprintf(stderr,"ERROR: incomplete record\n"); + return 1; + } + hashost = haspub = hassec = skipthis = 0; + continue; + } + + if (skipthis) + continue; + + char *start = line; + // trim whitespace + while (len != 0 && *start == ' ') { + ++start; + --len; + } + // find ':' + char *p = start; + for (;*p != '\0';++p) { + if (*p == ':') { + *p++ = '\0'; + goto foundkey; + } + } + // not `key: value` + fprintf(stderr,"ERROR: invalid syntax\n"); + return 1; // XXX could continue too there but eh + + foundkey: + + if (!strcmp(start,"hostname")) + keyt = HOST; + else if (!strcmp(start,"hs_ed25519_public_key")) + keyt = PUB; + else if (!strcmp(start,"hs_ed25519_secret_key")) + keyt = SEC; + else + continue; // uninterested + + // skip WS + while (*p == ' ') + ++p; + if (*p == '!') { + // skip ! tag + while (*p != '\0' && *p != ' ') + ++p; + // skip WS + while (*p == ' ') + ++p; + } + len = strlen(p); + switch (keyt) { + case HOST: + if (len != ONION_LEN) { + fprintf(stderr,"ERROR: invalid hostname syntax\n"); + return 1; + } + if (!hostname || !strcmp(hostname,p)) { + memcpy(&sname[direndpos],p,len + 1); + hashost = 1; + } else + skipthis = 1; + + break; + case PUB: + if (len != PUBKEY_LEN || !base64_valid(p,0)) { + fprintf(stderr,"ERROR: invalid pubkey syntax\n"); + return 1; + } + base64_from(pubbuf,p,len); + haspub = 1; + break; + case SEC: + if (len != SECKEY_LEN || !base64_valid(p,0)) { + fprintf(stderr,"ERROR: invalid seckey syntax\n"); + return 1; + } + base64_from(secbuf,p,len); + hassec = 1; + break; + } + if (hashost && haspub && hassec) { + if (createdir(sname,1) != 0) { + fprintf(stderr,"ERROR: could not create directory for key output\n"); + return 1; + } + + strcpy(&sname[onionendpos],"/hs_ed25519_secret_key"); + writetofile(sname,secbuf,FORMATTED_SECRET_LEN,1); + + strcpy(&sname[onionendpos],"/hs_ed25519_public_key"); + writetofile(sname,pubbuf,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) { + fwrite(&sname[printstartpos],printlen,1,fout); + fflush(fout); + } + if (hostname) + return 0; // finished + skipthis = 1; + } + } + + if (!feof(fin)) { + fprintf(stderr,"error while reading input\n"); + return 1; + } + + if (hostname) { + fprintf(stderr,"hostname wasn't found in input\n"); + return 1; + } + + return 0; +} diff --git a/yaml.h b/yaml.h new file mode 100644 index 0000000..0dc75d5 --- /dev/null +++ b/yaml.h @@ -0,0 +1,4 @@ +extern void yamlout_init(); +extern void yamlout_clean(); +extern void yamlout_writekeys(const char *hostname,const u8 *formated_public,const u8 *formated_secret); +extern int yamlin_parseandcreate(FILE *fin,char *sname,const char *hostname); From 83a63a30eef5cfb6da6a161d2854918915384991 Mon Sep 17 00:00:00 2001 From: cathugger Date: Mon, 9 Jul 2018 16:54:44 +0000 Subject: [PATCH 4/5] fixes --- base64_from.c | 3 +++ main.c | 32 ++++++++++++++++---------------- test_base64.c | 4 ++++ 3 files changed, 23 insertions(+), 16 deletions(-) diff --git a/base64_from.c b/base64_from.c index aabd84f..808bf1d 100644 --- a/base64_from.c +++ b/base64_from.c @@ -84,6 +84,9 @@ int base64_valid(const char *src,size_t *count) for (p = src;base64f[(u8)*p] != 0xFF;++p) ; + for (;*p == '=';++p) + ; + if (count) *count = (size_t) (p - src); return !*p; diff --git a/main.c b/main.c index 80e3df3..79552a3 100644 --- a/main.c +++ b/main.c @@ -667,22 +667,6 @@ int main(int argc,char **argv) exit(Q_FAILOPENOUTPUT); // define new err code? } - 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 (workdir) createdir(workdir,1); @@ -720,6 +704,22 @@ int main(int argc,char **argv) 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(); diff --git a/test_base64.c b/test_base64.c index cebb030..bd7fbf6 100644 --- a/test_base64.c +++ b/test_base64.c @@ -32,6 +32,10 @@ int main(void) tests0[i].in, buf, tests0[i].out); return 1; } + if (!base64_valid(buf,0)) { + printf("encoded data is considered invalid\n"); + return 3; + } r = base64_from((u8 *)buf2, buf, strlen(buf)); buf2[r] = '\0'; if (strcmp(buf2, tests0[i].rev) != 0) { From 04fadf33335e7b76a57f9c56e4beeeb31e4afc29 Mon Sep 17 00:00:00 2001 From: cathugger Date: Mon, 9 Jul 2018 17:00:51 +0000 Subject: [PATCH 5/5] fix docs --- main.c | 2 +- yaml.c | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/main.c b/main.c index 79552a3..041ccd2 100644 --- a/main.c +++ b/main.c @@ -417,7 +417,7 @@ static void printhelp(FILE *out,const char *progname) "\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 key file and extract key(s) to filesystem\n" + "\t-Y [filename [host.onion]] - parse yaml encoded input and extract key(s) to filesystem\n" ,progname,progname); fflush(out); } diff --git a/yaml.c b/yaml.c index cdd418e..31ea949 100644 --- a/yaml.c +++ b/yaml.c @@ -207,7 +207,6 @@ int yamlin_parseandcreate(FILE *fin,char *sname,const char *hostname) hashost = 1; } else skipthis = 1; - break; case PUB: if (len != PUBKEY_LEN || !base64_valid(p,0)) {