diff --git a/tools/sxtkeygen.c b/tools/sxtkeygen.c index 4104a70..b5cb37e 100644 --- a/tools/sxtkeygen.c +++ b/tools/sxtkeygen.c @@ -26,15 +26,266 @@ #include #include #include +#include #include #include #include #include +#include #include +#include +#include #include +#include + +#define MAX_PATHNAME 4096 +#define MAX_FNAME 128 +#define MAX_NAMEPREFIX 90 + +#define FULL_PROGRAM_NAME "SXT key container generation tool" + +static void __help_print(FILE *fso, const char *fmtname) +{ + fprintf(fso, "\n%s\n\n", FULL_PROGRAM_NAME); + + /* usage options */ + fprintf(fso, "Usage:\n"); + fprintf(fso, "\t%s [-t | --type ] [-n | --name ] [ -P | --path ]" + " [-H | --hash <64bit hash>] [-p | --passphrase]\n", fmtname); + + /* defaults */ + fprintf(fso, "\t%s -h | --help\n", fmtname); + fprintf(fso, "\t%s -v | --version\n", fmtname); + + /* options description */ + fprintf(fso, "\nOptions:\n"); + fprintf(fso, "\t%-25s Output key type:\n" + "\t\t\t\t\t * ppkp-ed25519 (default)\n", + "-t | --type "); + fprintf(fso, "\t%-25s Prefix for the output file names.\n", + "-n | --name "); + fprintf(fso, "\t%-25s Directory path where keys will be located (defaults: ~).\n", + "-P | --path "); + fprintf(fso, "\t%-25s Include special custom 64bit hash (defaults: none).\n", "-H | --hash <64bit hash>"); + fprintf(fso, "\t%-25s Ask a passphrase to encrypt a private key (defaults: key not encrypted).\n", + "-p | --passphrase"); + + fprintf(fso, "\t%-25s Show help screen.\n", "-h | --help"); + fprintf(fso, "\t%-25s Print version information.\n", "-v | --version"); + + return; +} + +static int __passkey_promt(char *passbuf, size_t p_len, int cnf, void *priv) +{ + char *promt = (char *)priv; + int sym, len = 0; + struct termios tio; + + /* print promt */ + fprintf(stdout, "%s", promt); + fflush(stdout); + + tcgetattr(STDIN_FILENO, &tio); + /* disable echo */ + tio.c_lflag &= ~(ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &tio); + + while((sym = fgetc(stdin)) != '\n') { + passbuf[len] = sym; + if(len + 1 == p_len) break; + else len++; + } + + /* enable again */ + tio.c_lflag |= (ICANON | ECHO); + tcsetattr(STDIN_FILENO, TCSANOW, &tio); + fprintf(stdout, "\n"); + + return SXT_SUCCESS; +} int main(int argc, char **argv) { + uint64_t hash = 0; + sxtkey_t *pair, *pubkey, *privkey; + char *dir = NULL, *nameprefix = NULL, *keytype = NULL; + char fullpath[MAX_PATHNAME]; + char privname[MAX_FNAME]; + char pubname[MAX_FNAME]; + struct stat statbuf; + int encrypt = 0, opt, type = 0, r; + + pair = pubkey = privkey = NULL; + + while(1) { + int option_index = 0; + static struct option long_options[] = { + /* These options a generic ones. */ + {"help", no_argument, NULL, 'h'}, /* print out help and version info */ + {"version", no_argument, NULL, 'v'}, /* just out a version info */ + /* key generation options */ + {"type", required_argument, NULL, 't'}, + {"name", required_argument, NULL, 'n'}, + {"path", required_argument, NULL, 'P'}, + {"hash", required_argument, NULL, 'H'}, + {"passphrase", no_argument, NULL, 'p'}, + /* termnil */ + {NULL, 0, NULL, 0}, + }; + + if((opt = getopt_long(argc, argv, "hvt:n:P:H:p", long_options, + &option_index)) == -1) break; + + switch(opt) { + case 'h': + __help_print(stdout, argv[0]); + return 0; + break; + case 'v': + /* TODO: add version output */ + return 0; + break; + case 't': keytype = optarg; break; + case 'n': nameprefix = optarg; break; + case 'P': dir = optarg; break; + case 'H': hash = strtoul(optarg, NULL, 0); break; + case 'p': encrypt = 1; break; + default: + fprintf(stderr, "Aborting.\n"); + __help_print(stdout, argv[0]); + abort(); + } + } + + /* init library */ + if((r = sxt_init())) { + fprintf(stderr, "Unable to init sxt library(%d).\nAborting.\n", r); + abort(); + } + + /* check type */ + if(keytype) type = sxt_key_type_fname(keytype); + else type = PPKP_ED25519; + + keytype = (char *)sxt_key_name(type); + + if(!type) { + fprintf(stderr, "Illegal keytype.\nAborting.\n"); + abort(); + } + + /* going to check output directory */ + if(!dir) dir = getenv("HOME"); + if(!dir) { + fprintf(stderr, "Cannot get output directory.\nAborting.\n"); + abort(); + } + + /* check if this is a directory */ + if(stat(dir, &statbuf)) { + fprintf(stderr, "Cannot stat given directory (%s).\nAborting.\n", + strerror(errno)); + abort(); + } else if(!S_ISDIR(statbuf.st_mode)) { + fprintf(stderr, "Given path isn't a directory.\nAborting.\n"); + abort(); + } + + if(nameprefix) { + if(strlen(nameprefix) > MAX_NAMEPREFIX) { + fprintf(stderr, "Name prefix too long (max: %d).\nAborting.\n", MAX_NAMEPREFIX); + abort(); + } + snprintf(privname, MAX_FNAME, "%s-%s.ppkp", nameprefix, keytype); + snprintf(pubname, MAX_FNAME, "%s-%s.pub", nameprefix, keytype); + } else { + snprintf(privname, MAX_FNAME, "%s.ppkp", keytype); + snprintf(pubname, MAX_FNAME, "%s.pub", keytype); + } + + /* ok let's deal with files */ + snprintf(fullpath, MAX_PATHNAME, "%s/%s", dir, privname); /* private key file output */ + if(!stat(fullpath, &statbuf)) { + fprintf(stderr, "Private key file '%s' already exists.\nAborting.\n", fullpath); + abort(); + } + snprintf(fullpath, MAX_PATHNAME, "%s/%s", dir, pubname); /* public key file output */ + if(!stat(fullpath, &statbuf)) { + fprintf(stderr, "Public key file '%s' already exists.\nAborting.\n", fullpath); + abort(); + } + + /* looks like all is fine */ + fprintf(stdout, "Going to generate %s keys:\n", keytype); + fprintf(stdout, "Output will located in '%s':\n", dir); + fprintf(stdout, "\t * Private key: %s\n", privname); + fprintf(stdout, "\t * Public key: %s\n", pubname); + if(encrypt) { + fprintf(stdout, "Private key going to be encrypted.\n"); + fprintf(stdout, "Passkey phrase will be asked later.\n"); + } + + /* generate a key first */ + if(!(pair = sxt_key_alloc())) { + fprintf(stderr, "Not enough memory to allocate a key.\nAborting.\n"); + abort(); + } + fprintf(stdout, "Generating key pair ..."); + if((r = sxt_key_generate(pair, type, 0)) != SXT_SUCCESS) { + fprintf(stderr, "FAIL.\nError (%d).\nAborting.\n", r); + sxt_key_free(pair); + abort(); + } else fprintf(stdout, "DONE.\n"); + + /* hash */ + if(hash) sxt_key_assign_hash(pair, hash); + + /* duplicate private */ + if((r = sxt_key_dup_private(pair, &privkey)) != SXT_SUCCESS) { + fprintf(stderr, "Unable to duplicate private key(%d).\nAborting.\n", r); + sxt_key_free(pair); + abort(); + } + /* get public duplicate */ + if((r = sxt_key_dup_public(pair, &pubkey)) != SXT_SUCCESS) { + fprintf(stderr, "Unable to duplicate public key(%d).\nAborting.\n", r); + sxt_key_free(pair); + sxt_key_free(privkey); + abort(); + } + + /* ok, output private key first */ + snprintf(fullpath, MAX_PATHNAME, "%s/%s", dir, privname); + if(!encrypt) { /* will not encrypt */ + r = sxt_key_export_priv_file(privkey, fullpath, NULL, NULL, NULL); + } else { + r = sxt_key_export_priv_file(privkey, fullpath, NULL, __passkey_promt, + (void *)"Enter passkey phrase:"); + } + + if(r != SXT_SUCCESS) { + __failed_export: + fprintf(stderr, "Unable to perform key export (%d).\nAborting.\n", r); + sxt_key_burn(pair); + sxt_key_burn(privkey); + sxt_key_free(pair); + sxt_key_free(privkey); + abort(); + } + + /* ok, output public key */ + snprintf(fullpath, MAX_PATHNAME, "%s/%s", dir, pubname); + r = sxt_key_export_public_file(pubkey, fullpath); + if(r != SXT_SUCCESS) goto __failed_export; + + sxt_key_burn(pair); + sxt_key_burn(privkey); + sxt_key_burn(pubkey); + sxt_key_free(pair); + sxt_key_free(privkey); + sxt_key_free(pubkey); + return 0; }