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);