From e2b152345ac6f46011b76fc2f104c04834fce45e Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Tue, 4 Feb 2014 11:15:27 +0100 Subject: [PATCH] implement import-key action --- cmdline.ggo | 5 +- yubico-piv-tool.c | 144 ++++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 144 insertions(+), 5 deletions(-) diff --git a/cmdline.ggo b/cmdline.ggo index 119f433..ba70f45 100644 --- a/cmdline.ggo +++ b/cmdline.ggo @@ -27,9 +27,12 @@ option "verbose" v "Print more information" int optional default="0" argoptional option "reader" r "Only use a matching reader" string optional option "key" k "Authentication key to use" string optional default="010203040506070801020304050607080102030405060708" -option "action" a "Action to take" values="version","generate","set-mgm-key","reset","pin-retries" enum +option "action" a "Action to take" values="version","generate","set-mgm-key","reset","pin-retries","import-key" enum option "slot" s "What key slot to operate on" values="9a","9c","9d","9e" enum optional option "algorithm" A "What algorithm to use" values="RSA1024","RSA2048","ECCP256" enum optional default="RSA2048" option "new-key" n "New authentication key to use" string optional option "pin-retries" p "Number of retries before the pin code is blocked" int optional option "puk-retries" P "Number of retries before the puk code is blocked" int optional +option "input" i "Filename to use as input, - for stdin" string optional default="-" +option "output" o "Filename to use as output, - for stdout" string optional default="-" +option "key-format" K "Format of the private key being read" values="PEM" enum optional default="PEM" diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index 4266681..5eb1d40 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -33,6 +33,7 @@ #include #include +#include #ifdef __APPLE__ #include @@ -63,8 +64,9 @@ union u_APDU { typedef union u_APDU APDU; -void dump_hex(unsigned const char*, unsigned int); -int send_data(SCARDHANDLE*, APDU, unsigned int, unsigned char*, unsigned long*, int); +static void dump_hex(unsigned const char*, unsigned int); +static int send_data(SCARDHANDLE*, APDU, unsigned int, unsigned char*, unsigned long*, int); +static int set_length(unsigned char*, int); static bool connect_reader(SCARDHANDLE *card, SCARDCONTEXT *context, const char *wanted, int verbose) { unsigned long num_readers; @@ -343,7 +345,116 @@ static bool set_pin_retries(SCARDHANDLE *card, int pin_retries, int puk_retries, return false; } -int send_data(SCARDHANDLE *card, APDU apdu, unsigned int send_len, unsigned char *data, unsigned long *recv_len, int verbose) { +static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, const char *input_file_name, const char *slot, int verbose) { + int key = 0; + FILE *input_file; + EVP_PKEY *private_key; + + sscanf(slot, "%x", &key); + + if(!strcmp(input_file_name, "-")) { + input_file = stdin; + } else { + input_file = fopen(input_file_name, "r"); + if(!input_file) { + fprintf(stderr, "Failed opening '%s'!\n", input_file_name); + return false; + } + } + + if(key_format == key_format_arg_PEM) { + private_key = PEM_read_PrivateKey(input_file, NULL, NULL, NULL); + if(!private_key) { + fprintf(stderr, "Failed loading private key for import.\n"); + return false; + } + } else { + /* TODO: more formats go here */ + fprintf(stderr, "Unknown key format.\n"); + return false; + } + + { + int type = EVP_PKEY_type(private_key->type); + if(type == EVP_PKEY_RSA) { + int algorithm; + RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); + int size = RSA_size(rsa_private_key); + if(size == 256) { + algorithm = 7; + } else if(size == 128) { + algorithm = 6; + } else { + fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); + return false; + } + { + APDU apdu; + unsigned char in_data[1024]; + unsigned char *in_ptr = in_data; + int sw; + int in_size; + + *in_ptr++ = 0x01; + in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->p)); + in_ptr += BN_bn2bin(rsa_private_key->p, in_ptr); + + *in_ptr++ = 0x02; + in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->q)); + in_ptr += BN_bn2bin(rsa_private_key->q, in_ptr); + + *in_ptr++ = 0x03; + in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->dmp1)); + in_ptr += BN_bn2bin(rsa_private_key->dmp1, in_ptr); + + *in_ptr++ = 0x04; + in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->dmq1)); + in_ptr += BN_bn2bin(rsa_private_key->dmq1, in_ptr); + + *in_ptr++ = 0x05; + in_ptr += set_length(in_ptr, BN_num_bytes(rsa_private_key->iqmp)); + in_ptr += BN_bn2bin(rsa_private_key->iqmp, in_ptr); + + in_size = in_ptr - in_data; + in_ptr = in_data; + + while(in_ptr < in_data + in_size) { + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + size_t this_size = 0xff; + memset(apdu.raw, 0, sizeof(apdu)); + if(in_ptr + 0xff < in_data + in_size) { + apdu.st.cla = 0x10; + } else { + this_size = (size_t)((in_data + in_size) - in_ptr); + } + if(verbose) { + fprintf(stderr, "going to send %zu bytes in this go.\n", this_size); + } + apdu.st.ins = 0xfe; + apdu.st.p1 = algorithm; + apdu.st.p2 = key; + apdu.st.lc = this_size; + memcpy(apdu.st.data, in_ptr, this_size); + sw = send_data(card, apdu, this_size + 5, data, &recv_len, verbose); + if(sw != 0x9000) { + fprintf(stderr, "Failed import command with code %x.", sw); + return false; + } + in_ptr += this_size; + } + } + + } else { + /* TODO: ECC */ + fprintf(stderr, "Unknown type: %d\n", type); + return false; + } + } + return true; +} + +static int send_data(SCARDHANDLE *card, APDU apdu, unsigned int send_len, unsigned char *data, unsigned long *recv_len, int verbose) { long rc; int sw; @@ -371,7 +482,7 @@ int send_data(SCARDHANDLE *card, APDU apdu, unsigned int send_len, unsigned char return sw; } -void dump_hex(const unsigned char *buf, unsigned int len) { +static void dump_hex(const unsigned char *buf, unsigned int len) { unsigned int i; for (i = 0; i < len; i++) { fprintf(stderr, "%02x ", buf[i]); @@ -403,6 +514,22 @@ static bool parse_key(char *key_arg, unsigned char *key, int verbose) { return true; } +static int set_length(unsigned char *buffer, int length) { + if(length < 0x80) { + *buffer++ = length; + return 1; + } else if(length < 0xff) { + *buffer++ = 0x81; + *buffer++ = length; + return 2; + } else { + *buffer++ = 0x82; + *buffer++ = (length >> 8) & 0xff; + *buffer++ = length & 0xff; + return 3; + } +} + int main(int argc, char *argv[]) { struct gengetopt_args_info args_info; SCARDHANDLE card; @@ -466,6 +593,15 @@ int main(int argc, char *argv[]) { } else { return EXIT_FAILURE; } + } else if(args_info.action_arg == action_arg_importMINUS_key) { + if(args_info.slot_arg != slot__NULL) { + if(import_key(&card, args_info.key_format_arg, args_info.input_arg, args_info.slot_orig, verbosity) == false) { + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "The generate action needs a slot (-s) to operate on.\n"); + return EXIT_FAILURE; + } } return EXIT_SUCCESS;