From a7d5f1ed0a6ddc8f489f3c9cb7622bba5a5eef7b Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 6 Feb 2014 09:06:09 +0100 Subject: [PATCH 1/6] start on request certificate action --- cmdline.ggo | 3 +- yubico-piv-tool.c | 138 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+), 1 deletion(-) diff --git a/cmdline.ggo b/cmdline.ggo index 28bfd7f..3a5d990 100644 --- a/cmdline.ggo +++ b/cmdline.ggo @@ -27,7 +27,7 @@ option "verbose" v "Print more information" int optional default="0" argoptional option "reader" r "Only use a matching reader" string optional default="Yubikey" 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","import-key","import-certificate","set-chuid" enum multiple +option "action" a "Action to take" values="version","generate","set-mgm-key","reset","pin-retries","import-key","import-certificate","set-chuid","request-certificate" enum multiple 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 @@ -37,3 +37,4 @@ 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 key being read/written" values="PEM","PKCS12" enum optional default="PEM" option "password" p "Password for decryption of private key file" string optional +option "subject" S "The subject to use for certificate request" string optional diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index c6e7d68..35bdae8 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -86,6 +86,7 @@ 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 int get_length(unsigned char*, int *); +static X509_NAME *parse_name(char*); static bool connect_reader(SCARDHANDLE *card, SCARDCONTEXT *context, const char *wanted, int verbose) { unsigned long num_readers; @@ -811,6 +812,130 @@ static bool set_chuid(SCARDHANDLE *card, int verbose) { return true; } +static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_format, + const char *input_file_name, const char *slot, char *subject, int verbose) { + X509_REQ *req = NULL; + X509_NAME *name = NULL; + FILE *input_file; + EVP_PKEY *public_key = NULL; + bool ret = true; + EVP_MD *md_alg = NULL; + X509_ALGOR algor; + + 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) { + public_key = PEM_read_PUBKEY(input_file, NULL, NULL, NULL); + if(!public_key) { + fprintf(stderr, "Failed loading private key for import.\n"); + ret = false; + goto request_out; + } + } else { + fprintf(stderr, "Only PEM supported for public key input.\n"); + ret = false; + goto request_out; + } + req = X509_REQ_new(); + if(!req) { + fprintf(stderr, "Failed to allocate request structure.\n"); + ret = false; + goto request_out; + } + if(!X509_REQ_set_pubkey(req, public_key)) { + fprintf(stderr, "Failed setting the request public key.\n"); + ret = false; + goto request_out; + } + + name = parse_name(subject); + if(!name) { + fprintf(stderr, "Failed encoding subject as name.\n"); + ret = false; + goto request_out; + } + if(!X509_REQ_set_subject_name(req, name)) { + fprintf(stderr, "Failed setting the request subject.\n"); + ret = false; + goto request_out; + } + + md_alg = EVP_get_digestbyname("sha1"); + if(!md_alg) { + fprintf(stderr, "No digest.\n"); + ret = false; + goto request_out; + } + X509_ALGOR_set_md(req->sig_alg, md_alg); + + X509_REQ_print_fp(stdout,req); + + +request_out: + if(input_file != stdin) { + fclose(input_file); + } + if(public_key) { + EVP_PKEY_free(public_key); + } + if(req) { + X509_REQ_free(req); + } + if(name) { + X509_NAME_free(name); + } + return ret; +} + +static X509_NAME *parse_name(char *name) { + X509_NAME *parsed = NULL; + char *ptr = name; + char *part; + char *saveptr = NULL; + if(*name != '/') { + fprintf(stderr, "Name does not start with '/'!\n"); + return NULL; + } + parsed = X509_NAME_new(); + if(!parsed) { + fprintf(stderr, "Failed to allocate memory\n"); + return NULL; + } + while((part = strtok_r(ptr, "/", &saveptr))) { + char *key; + char *value; + char *innersave = NULL; + + ptr = NULL; + key = strtok_r(part, "=", &innersave); + if(!key) { + fprintf(stderr, "Malformed name (%s)\n", part); + goto parse_err; + } + value = strtok_r(NULL, "=", &innersave); + if(!value) { + fprintf(stderr, "Malformed name (%s)\n", part); + goto parse_err; + } + if(!X509_NAME_add_entry_by_txt(parsed, key, MBSTRING_ASC, (unsigned char*)value, -1, -1, 0)) { + fprintf(stderr, "Failed adding %s=%s to name.\n", key, value); + goto parse_err; + } + } + return parsed; +parse_err: + X509_NAME_free(parsed); + return NULL; +} + static int send_data(SCARDHANDLE *card, APDU *apdu, unsigned int send_len, unsigned char *data, unsigned long *recv_len, int verbose) { long rc; @@ -1021,6 +1146,19 @@ int main(int argc, char *argv[]) { } printf("Successfully set new CHUID.\n"); break; + case action_arg_requestMINUS_certificate: + if(args_info.slot_arg == slot__NULL) { + fprintf(stderr, "The request-certificate action needs a slot (-s) to operate on.\n"); + return EXIT_FAILURE; + } else if(!args_info.subject_arg) { + fprintf(stderr, "The request-certificate action needs a subject (-S) to operate on.\n"); + } else { + if(request_certificate(&card, args_info.key_format_arg, args_info.input_arg, + args_info.slot_orig, args_info.subject_arg, verbosity) == false) { + return EXIT_FAILURE; + } + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action); From be9211dc5b44eb79e7a04cf7f17a6a2ef7a371c2 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 6 Feb 2014 09:30:59 +0100 Subject: [PATCH 2/6] more work on request --- yubico-piv-tool.c | 37 +++++++++++++++++++++++++++++++------ 1 file changed, 31 insertions(+), 6 deletions(-) diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index 35bdae8..a3c6ca1 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -813,14 +813,18 @@ static bool set_chuid(SCARDHANDLE *card, int verbose) { } static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_format, - const char *input_file_name, const char *slot, char *subject, int verbose) { + const char *input_file_name, const char *slot, char *subject, + const char *output_file_name, int verbose) { X509_REQ *req = NULL; X509_NAME *name = NULL; FILE *input_file; + FILE *output_file; EVP_PKEY *public_key = NULL; bool ret = true; - EVP_MD *md_alg = NULL; + const EVP_MD *md_alg = NULL; X509_ALGOR algor; + unsigned char digest[20]; + unsigned int digest_len = sizeof(digest); if(!strcmp(input_file_name, "-")) { input_file = stdin; @@ -831,6 +835,15 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form return false; } } + if(!strcmp(output_file_name, "-")) { + output_file = stdout; + } else { + output_file = fopen(output_file_name, "w"); + if(!output_file) { + fprintf(stderr, "Failed opening '%s'!\n", output_file_name); + return false; + } + } if(key_format == key_format_arg_PEM) { public_key = PEM_read_PUBKEY(input_file, NULL, NULL, NULL); @@ -870,19 +883,31 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form md_alg = EVP_get_digestbyname("sha1"); if(!md_alg) { - fprintf(stderr, "No digest.\n"); + fprintf(stderr, "Unable to get a sha1 digest.\n"); + ret = false; + goto request_out; + } + if(!X509_REQ_digest(req, md_alg, digest, &digest_len)) { + fprintf(stderr, "Failed doing digest of request.\n"); ret = false; goto request_out; } X509_ALGOR_set_md(req->sig_alg, md_alg); - X509_REQ_print_fp(stdout,req); - + X509_REQ_print_fp(output_file, req); + if(verbose) { + fprintf(stderr, "computed digest as: "); + dump_hex(digest, digest_len); + fprintf(stderr, "\n"); + } request_out: if(input_file != stdin) { fclose(input_file); } + if(output_file != stdout) { + fclose(output_file); + } if(public_key) { EVP_PKEY_free(public_key); } @@ -1154,7 +1179,7 @@ int main(int argc, char *argv[]) { fprintf(stderr, "The request-certificate action needs a subject (-S) to operate on.\n"); } else { if(request_certificate(&card, args_info.key_format_arg, args_info.input_arg, - args_info.slot_orig, args_info.subject_arg, verbosity) == false) { + args_info.slot_orig, args_info.subject_arg, args_info.output_arg, verbosity) == false) { return EXIT_FAILURE; } } From 71cb66e5bf9430174b6fa30c62f9b6056ed1c397 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 6 Feb 2014 09:54:29 +0100 Subject: [PATCH 3/6] refactor so we get algorithm from a function. --- yubico-piv-tool.c | 191 +++++++++++++++++++++++++++------------------- 1 file changed, 113 insertions(+), 78 deletions(-) diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index a3c6ca1..77b4e60 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -87,6 +87,7 @@ static int send_data(SCARDHANDLE*, APDU*, unsigned int, unsigned char*, unsigned static int set_length(unsigned char*, int); static int get_length(unsigned char*, int *); static X509_NAME *parse_name(char*); +static unsigned char get_algorithm(EVP_PKEY*); static bool connect_reader(SCARDHANDLE *card, SCARDCONTEXT *context, const char *wanted, int verbose) { unsigned long num_readers; @@ -534,85 +535,72 @@ static bool import_key(SCARDHANDLE *card, enum enum_key_format key_format, } { - 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); - ret = false; - goto import_out; - } - if(verbose) { - fprintf(stderr, "Found RSA-%d key.\n", size * 8); - } - { - 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 %lu bytes in this go.\n", (unsigned long)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); - ret = false; - goto import_out; - } - in_ptr += this_size; - } - } - - } else { - /* TODO: ECC */ - fprintf(stderr, "Unknown type: %d\n", type); + unsigned char algorithm = get_algorithm(private_key); + if(algorithm == 11) { + fprintf(stderr, "import key only supports RSA.\n"); ret = false; + goto import_out; + } else if(algorithm == 0) { + ret = false; + goto import_out; + } + { + APDU apdu; + unsigned char in_data[1024]; + unsigned char *in_ptr = in_data; + int sw; + int in_size; + RSA *rsa_private_key = EVP_PKEY_get1_RSA(private_key); + + *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 %lu bytes in this go.\n", (unsigned long)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); + ret = false; + goto import_out; + } + in_ptr += this_size; + } } } import_out: @@ -825,6 +813,7 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form X509_ALGOR algor; unsigned char digest[20]; unsigned int digest_len = sizeof(digest); + unsigned char algorithm; if(!strcmp(input_file_name, "-")) { input_file = stdin; @@ -857,6 +846,8 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form ret = false; goto request_out; } + algorithm = get_algorithm(public_key); + req = X509_REQ_new(); if(!req) { fprintf(stderr, "Failed to allocate request structure.\n"); @@ -900,6 +891,16 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form dump_hex(digest, digest_len); fprintf(stderr, "\n"); } + { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + + memset(apdu.raw, 0, sizeof(apdu.raw)); + apdu.st.ins = 0x87; + apdu.st.p1 = algorithm; + } request_out: if(input_file != stdin) { @@ -920,6 +921,40 @@ request_out: return ret; } +static unsigned char get_algorithm(EVP_PKEY *key) { + int type = EVP_PKEY_type(key->type); + switch(type) { + case EVP_PKEY_RSA: + { + RSA *rsa = EVP_PKEY_get1_RSA(key); + int size = RSA_size(rsa); + if(size == 256) { + return 7; + } else if(size == 128) { + return 6; + } else { + fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); + return 0; + } + } + case EVP_PKEY_EC: + { + EC_KEY *ec = EVP_PKEY_get1_EC_KEY(key); + const EC_GROUP *group = EC_KEY_get0_group(ec); + int curve = EC_GROUP_get_curve_name(group); + if(curve == NID_X9_62_prime256v1) { + return 11; + } else { + fprintf(stderr, "Unknown EC curve %d\n", curve); + return 0; + } + } + default: + fprintf(stderr, "Unknown algorithm %d.\n", type); + return 0; + } +} + static X509_NAME *parse_name(char *name) { X509_NAME *parsed = NULL; char *ptr = name; From ca45b6a5653b45c33e793636b97538bf9d1644ff Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 6 Feb 2014 10:20:37 +0100 Subject: [PATCH 4/6] add a verify-pin action (needed for signing request) --- cmdline.ggo | 3 ++- yubico-piv-tool.c | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/cmdline.ggo b/cmdline.ggo index 3a5d990..ccb1676 100644 --- a/cmdline.ggo +++ b/cmdline.ggo @@ -27,7 +27,7 @@ option "verbose" v "Print more information" int optional default="0" argoptional option "reader" r "Only use a matching reader" string optional default="Yubikey" 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","import-key","import-certificate","set-chuid","request-certificate" enum multiple +option "action" a "Action to take" values="version","generate","set-mgm-key","reset","pin-retries","import-key","import-certificate","set-chuid","request-certificate","verify-pin" enum multiple 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 @@ -38,3 +38,4 @@ option "output" o "Filename to use as output, - for stdout" string optional defa option "key-format" K "Format of the key being read/written" values="PEM","PKCS12" enum optional default="PEM" option "password" p "Password for decryption of private key file" string optional option "subject" S "The subject to use for certificate request" string optional +option "pin" P "Pin code for verification" string optional diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index 77b4e60..7073fd6 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -921,6 +921,33 @@ request_out: return ret; } +static bool verify_pin(SCARDHANDLE *card, const char *pin, int verbose) { + APDU apdu; + unsigned char data[0xff]; + unsigned long recv_len = sizeof(data); + int sw; + int len = strlen(pin); + + if(len > 8) { + fprintf(stderr, "Maximum 8 digits of PIN supported.\n"); + } + + memset(apdu.raw, 0, sizeof(apdu.raw)); + apdu.st.ins = 0x20; + apdu.st.p1 = 0x00; + apdu.st.p2 = 0x80; + apdu.st.lc = 0x08; + memcpy(apdu.st.data, pin, len); + if(len < 8) { + memset(apdu.st.data + len, 0xff, 8 - len); + } + sw = send_data(card, &apdu, apdu.st.lc + 5, data, &recv_len, verbose); + if(sw != 0x9000) { + return false; + } + return true; +} + static unsigned char get_algorithm(EVP_PKEY *key) { int type = EVP_PKEY_type(key->type); switch(type) { @@ -1219,6 +1246,19 @@ int main(int argc, char *argv[]) { } } break; + case action_arg_verifyMINUS_pin: + if(args_info.pin_arg) { + if(verify_pin(&card, args_info.pin_arg, verbosity)) { + printf("Successfully verified PIN.\n"); + } else { + fprintf(stderr, "Failed to verify PIN.\n"); + return EXIT_FAILURE; + } + } else { + fprintf(stderr, "The verify-pin action needs a pin (-P).\n"); + return EXIT_FAILURE; + } + break; case action__NULL: default: fprintf(stderr, "Wrong action. %d.\n", action); From cfbb7020688b3de3f7428f1c3a0391752eb894b6 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Thu, 6 Feb 2014 10:20:50 +0100 Subject: [PATCH 5/6] more work on request, try to sign digest --- yubico-piv-tool.c | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index 7073fd6..dce1b9a 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -814,6 +814,9 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form unsigned char digest[20]; unsigned int digest_len = sizeof(digest); unsigned char algorithm; + int key = 0; + + sscanf(slot, "%x", &key); if(!strcmp(input_file_name, "-")) { input_file = stdin; @@ -847,6 +850,10 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form goto request_out; } algorithm = get_algorithm(public_key); + if(algorithm == 0) { + ret = false; + goto request_out; + } req = X509_REQ_new(); if(!req) { @@ -894,12 +901,21 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form { APDU apdu; unsigned char data[0xff]; + unsigned char *dataptr = apdu.st.data; unsigned long recv_len = sizeof(data); int sw; memset(apdu.raw, 0, sizeof(apdu.raw)); apdu.st.ins = 0x87; apdu.st.p1 = algorithm; + apdu.st.p2 = key; + apdu.st.lc = digest_len + 4; + *dataptr++ = 0x7c; + *dataptr++ = digest_len + 2; + *dataptr++ = 0x81; + *dataptr++ = digest_len; + memcpy(dataptr, digest, digest_len); + sw = send_data(card, &apdu, apdu.st.lc + 5, data, &recv_len, verbose); } request_out: From 4b64a5c800d6e3c01a6202831db2d7bd0bf55b24 Mon Sep 17 00:00:00 2001 From: Klas Lindfors Date: Fri, 7 Feb 2014 10:52:56 +0100 Subject: [PATCH 6/6] working certificate-requests --- yubico-piv-tool.c | 160 ++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 132 insertions(+), 28 deletions(-) diff --git a/yubico-piv-tool.c b/yubico-piv-tool.c index dce1b9a..612a0bf 100644 --- a/yubico-piv-tool.c +++ b/yubico-piv-tool.c @@ -66,6 +66,10 @@ unsigned const char chuid_tmpl[] = { }; #define CHUID_GUID_OFFS 35 +unsigned const char sha1oid[] = { + 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14 +}; + #define KEY_LEN 24 union u_APDU { @@ -271,7 +275,7 @@ static bool generate_key(SCARDHANDLE *card, const char *slot, enum enum_algorith if(!strcmp(output_file_name, "-")) { output_file = stdout; } else { - output_file = fopen(output_file_name, "r"); + output_file = fopen(output_file_name, "w"); if(!output_file) { fprintf(stderr, "Failed opening '%s'!\n", output_file_name); return false; @@ -805,16 +809,19 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form const char *output_file_name, int verbose) { X509_REQ *req = NULL; X509_NAME *name = NULL; + X509_ALGOR *algor = NULL; FILE *input_file; FILE *output_file; EVP_PKEY *public_key = NULL; bool ret = true; const EVP_MD *md_alg = NULL; - X509_ALGOR algor; - unsigned char digest[20]; - unsigned int digest_len = sizeof(digest); + unsigned char digest[35]; + unsigned int digest_len = 20; unsigned char algorithm; int key = 0; + ASN1_STRING *sig = NULL; + unsigned char foo[256]; + int len; sscanf(slot, "%x", &key); @@ -867,6 +874,8 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form goto request_out; } + X509_REQ_set_version(req, 0); + name = parse_name(subject); if(!name) { fprintf(stderr, "Failed encoding subject as name.\n"); @@ -879,43 +888,133 @@ static bool request_certificate(SCARDHANDLE *card, enum enum_key_format key_form goto request_out; } - md_alg = EVP_get_digestbyname("sha1"); - if(!md_alg) { - fprintf(stderr, "Unable to get a sha1 digest.\n"); - ret = false; - goto request_out; - } - if(!X509_REQ_digest(req, md_alg, digest, &digest_len)) { + algor = sk_X509_ALGOR_new_null(); + algor->parameter = sk_ASN1_TYPE_new_null(); + algor->algorithm=OBJ_nid2obj(NID_sha1WithRSAEncryption); + algor->parameter->type = V_ASN1_NULL; + + req->sig_alg = algor; + + memset(digest, 0, sizeof(digest)); + memcpy(digest, sha1oid, sizeof(sha1oid)); + /* XXX: this should probably use X509_REQ_digest() but that's buggy */ + if(!ASN1_item_digest(ASN1_ITEM_rptr(X509_REQ_INFO), EVP_sha1(), req->req_info, + digest + 15, &digest_len)) { fprintf(stderr, "Failed doing digest of request.\n"); ret = false; goto request_out; } - X509_ALGOR_set_md(req->sig_alg, md_alg); - X509_REQ_print_fp(output_file, req); if(verbose) { fprintf(stderr, "computed digest as: "); - dump_hex(digest, digest_len); + dump_hex(digest, sizeof(digest)); fprintf(stderr, "\n"); } + if(algorithm == 6) { + len = 128; + } else if(algorithm == 7) { + len = 256; + } + RSA_padding_add_PKCS1_type_1(foo, len, digest, sizeof(digest)); { - APDU apdu; - unsigned char data[0xff]; - unsigned char *dataptr = apdu.st.data; - unsigned long recv_len = sizeof(data); + unsigned char indata[1024]; + unsigned char *dataptr = indata; + unsigned char data[1024]; + unsigned long recv_len; + unsigned long received = 0; int sw; + int bytes; + int datasize; + + if(len < 0x80) { + bytes = 1; + } else if(len < 0xff) { + bytes = 2; + } else { + bytes = 3; + } - memset(apdu.raw, 0, sizeof(apdu.raw)); - apdu.st.ins = 0x87; - apdu.st.p1 = algorithm; - apdu.st.p2 = key; - apdu.st.lc = digest_len + 4; *dataptr++ = 0x7c; - *dataptr++ = digest_len + 2; + dataptr += set_length(dataptr, len + bytes + 3); + *dataptr++ = 0x82; + *dataptr++ = 0x00; *dataptr++ = 0x81; - *dataptr++ = digest_len; - memcpy(dataptr, digest, digest_len); - sw = send_data(card, &apdu, apdu.st.lc + 5, data, &recv_len, verbose); + dataptr += set_length(dataptr, len); + memcpy(dataptr, foo, len); + dataptr += len; + + datasize = dataptr - indata; + fprintf(stderr, "size is %d\n", datasize); + dataptr = indata; + while(dataptr < indata + datasize) { + size_t this_size = 0xff; + APDU apdu; + recv_len = 0xff; + + memset(apdu.raw, 0, sizeof(apdu.raw)); + if(dataptr + 0xff < indata + datasize) { + apdu.st.cla = 0x10; + } else { + this_size = (size_t)((indata + datasize) - dataptr); + } + if(verbose) { + fprintf(stderr, "going to send %lu bytes in this go.\n", (unsigned long)this_size); + } + + apdu.st.ins = 0x87; + apdu.st.p1 = algorithm; + apdu.st.p2 = key; + apdu.st.lc = this_size; + memcpy(apdu.st.data, dataptr, this_size); + sw = send_data(card, &apdu, apdu.st.lc + 5, data, &recv_len, verbose); + if((sw & 0x6100) == 0x6100) { + received += recv_len - 2; + recv_len = 0xff; + memset(apdu.raw, 0, sizeof(apdu)); + apdu.st.ins = 0xc0; + sw = send_data(card, &apdu, 4, data + received, &recv_len, verbose); + if(sw == 0x9000) { + received += recv_len - 2; + } else { + fprintf(stderr, "Failed sign command with code %x.\n", sw); + ret = false; + goto request_out; + } + } else if(sw != 0x9000) { + fprintf(stderr, "Failed sign command with code %x.\n", sw); + ret = false; + goto request_out; + } + dataptr += this_size; + } + + /* skip the first 7c tag */ + if(data[0] != 0x7c) { + fprintf(stderr, "Failed parsing signature reply.\n"); + ret = false; + goto request_out; + } + dataptr = data + 1; + dataptr += get_length(dataptr, &len); + /* skip the 82 tag */ + if(*dataptr != 0x82) { + fprintf(stderr, "Failed parsing signature reply.\n"); + ret = false; + goto request_out; + } + dataptr++; + dataptr += get_length(dataptr, &len); + sig = M_ASN1_BIT_STRING_new(); + M_ASN1_BIT_STRING_set(sig, dataptr, len); + req->signature = sig; + + if(key_format == key_format_arg_PEM) { + PEM_write_X509_REQ(output_file, req); + } else { + fprintf(stderr, "Only PEM support available for certificate requests.\n"); + ret = false; + goto request_out; + } } request_out: @@ -934,6 +1033,9 @@ request_out: if(name) { X509_NAME_free(name); } + if(algor) { + X509_ALGOR_free(algor); + } return ret; } @@ -972,8 +1074,10 @@ static unsigned char get_algorithm(EVP_PKEY *key) { RSA *rsa = EVP_PKEY_get1_RSA(key); int size = RSA_size(rsa); if(size == 256) { + printf("rsa 2048\n"); return 7; } else if(size == 128) { + printf("rsa 1024\n"); return 6; } else { fprintf(stderr, "Unuseable key of %d bits, only 1024 and 2048 is supported.\n", size * 8); @@ -1028,7 +1132,7 @@ static X509_NAME *parse_name(char *name) { fprintf(stderr, "Malformed name (%s)\n", part); goto parse_err; } - if(!X509_NAME_add_entry_by_txt(parsed, key, MBSTRING_ASC, (unsigned char*)value, -1, -1, 0)) { + if(!X509_NAME_add_entry_by_txt(parsed, key, MBSTRING_UTF8, (unsigned char*)value, -1, -1, 0)) { fprintf(stderr, "Failed adding %s=%s to name.\n", key, value); goto parse_err; }