From 6a416795b7afd03fe0d4fec69759faae3e300f49 Mon Sep 17 00:00:00 2001 From: heios Date: Fri, 6 Jul 2018 01:20:51 +0100 Subject: [PATCH] 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; +}