You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
292 lines
8.5 KiB
C
292 lines
8.5 KiB
C
/*
|
|
* Secure X Message Passing Library tools.
|
|
*
|
|
* (c) Alexander Vdolainen 2016 <avdolainen@zoho.com>
|
|
*
|
|
* libsxmp is free software: you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published
|
|
* by the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* libsxmp is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
|
* See the GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.";
|
|
*
|
|
* sxtkeygen - sxt key container tool for generating the keys
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <dirent.h>
|
|
#define __USE_GNU
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <stdarg.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <limits.h>
|
|
#include <unistd.h>
|
|
#include <execinfo.h>
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
#include <errno.h>
|
|
#include <termios.h>
|
|
|
|
#include <sxt/errno.h>
|
|
#include <sxt/sxtkey.h>
|
|
#include <sxt/sxt.h>
|
|
|
|
#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 <type>] [-n | --name <name-prefix>] [ -P | --path <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 <type>");
|
|
fprintf(fso, "\t%-25s Prefix for the output file names.\n",
|
|
"-n | --name <name-prefix>");
|
|
fprintf(fso, "\t%-25s Directory path where keys will be located (defaults: ~).\n",
|
|
"-P | --path <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;
|
|
}
|