diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index 3663460d..ddd7d504 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -24,15 +24,17 @@ #include "config.h" -#ifdef ENABLE_OPENSSL /* empty file without openssl */ - #include #include #include +#include +#ifdef ENABLE_OPENSSL + /* openssl only needed for card administration */ #include #include #include #include +#endif /* ENABLE_OPENSSL */ #include "internal.h" #include "asn1.h" @@ -53,17 +55,71 @@ enum { PIV_OBJ_X509_KM, PIV_OBJ_X509_CARD_AUTH, PIV_OBJ_SEC_OBJ, + PIV_OBJ_DISCOVERY, + PIV_OBJ_HISTORY, + PIV_OBJ_RETIRED_X509_1, + PIV_OBJ_RETIRED_X509_2, + PIV_OBJ_RETIRED_X509_3, + PIV_OBJ_RETIRED_X509_4, + PIV_OBJ_RETIRED_X509_5, + PIV_OBJ_RETIRED_X509_6, + PIV_OBJ_RETIRED_X509_7, + PIV_OBJ_RETIRED_X509_8, + PIV_OBJ_RETIRED_X509_9, + PIV_OBJ_RETIRED_X509_10, + PIV_OBJ_RETIRED_X509_11, + PIV_OBJ_RETIRED_X509_12, + PIV_OBJ_RETIRED_X509_13, + PIV_OBJ_RETIRED_X509_14, + PIV_OBJ_RETIRED_X509_15, + PIV_OBJ_RETIRED_X509_16, + PIV_OBJ_RETIRED_X509_17, + PIV_OBJ_RETIRED_X509_18, + PIV_OBJ_RETIRED_X509_19, + PIV_OBJ_RETIRED_X509_20, + PIV_OBJ_IRIS_IMAGE, PIV_OBJ_9B03, PIV_OBJ_9A06, PIV_OBJ_9C06, PIV_OBJ_9D06, PIV_OBJ_9E06, + PIV_OBJ_8206, + PIV_OBJ_8306, + PIV_OBJ_8406, + PIV_OBJ_8506, + PIV_OBJ_8606, + PIV_OBJ_8706, + PIV_OBJ_8806, + PIV_OBJ_8906, + PIV_OBJ_8A06, + PIV_OBJ_8B06, + PIV_OBJ_8C06, + PIV_OBJ_8D06, + PIV_OBJ_8E06, + PIV_OBJ_8F06, + PIV_OBJ_9006, + PIV_OBJ_9106, + PIV_OBJ_9206, + PIV_OBJ_9306, + PIV_OBJ_9406, + PIV_OBJ_9506, PIV_OBJ_LAST_ENUM }; -/* flags in the piv_obj_cache */ +/* + * Flags in the piv_obj_cache: + * PIV_OBJ_CACHE_VALID means the data in the cache can be used. + * It might have zero length indicating that the object was not found. + * PIV_OBJ_CACHE_NOT_PRESENT means do not even try to read the object. + * These objects will only be present if the history object says + * they are on the card, or the discovery or history object in not present. + * If the file lilsted in the history object offCardCertURL was found, + * its certs will be read into the cache and PIV_OBJ_CACHE_VALID set + * and PIV_OBJ_CACHE_NOT_PRESENT unset. + */ -#define PIV_OBJ_CACHE_VALID 1 +#define PIV_OBJ_CACHE_VALID 1 +#define PIV_OBJ_CACHE_NOT_PRESENT 8 typedef struct piv_obj_cache { u8* obj_data; @@ -74,7 +130,6 @@ typedef struct piv_obj_cache { } piv_obj_cache_t; typedef struct piv_private_data { - struct sc_pin_cmd_pin pin_info; sc_file_t *aid_file; int enumtag; int selected_obj; /* The index into the piv_objects last selected */ @@ -85,6 +140,10 @@ typedef struct piv_private_data { u8* w_buf; /* write_binary buffer */ size_t w_buf_len; /* length of w_buff */ piv_obj_cache_t obj_cache[PIV_OBJ_LAST_ENUM]; + int keysWithOnCardCerts; + int keysWithOffCardCerts; + char * offCardCertURL; + int pin_preference; /* set from Discovery object */ } piv_private_data_t; #define PIV_DATA(card) ((piv_private_data_t*)card->drv_data) @@ -96,23 +155,33 @@ struct piv_aid { u8 *value; }; -/* The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " +/* + * The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " * NIST published this on 10/6/2005 - * 800-73-2 is due for release 11/2007. * 800-73-2 Part 1 now refers to version "02 00" * i.e. "A0 00 00 03 08 00 00 01 00 02 00". * but we dont need the version number. but could get it from the PIX. + * + * 800-73-3 Part 1 now referes to "01 00" i.e. going back to 800-73-1. + * The main differences between 73-1, and 73-3 are the addition of the + * key History object and keys, as well as Discovery and Iris objects. */ + static struct piv_aid piv_aids[] = { {SC_CARD_TYPE_PIV_II_GENERIC, 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00" }, {0, 9, 0, NULL } }; -/* flags in the piv_object */ +/* + * Flags in the piv_object: + * PIV_OBJECT_NOT_PRESENT: the presents of the object is + * indicated by the History object. + */ #define PIV_OBJECT_TYPE_CERT 1 #define PIV_OBJECT_TYPE_PUBKEY 2 +#define PIV_OBJECT_NOT_PRESENT 4 struct piv_object { int enumtag; @@ -125,7 +194,7 @@ struct piv_object { }; /* Must be in order, and one per enumerated PIV_OBJ */ -static struct piv_object piv_objects[] = { +static const struct piv_object piv_objects[] = { { PIV_OBJ_CCC, "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", 3, "\x5F\xC1\x07", "\xDB\x00", 0}, { PIV_OBJ_CHUI, "Card Holder Unique Identifier", @@ -134,12 +203,11 @@ static struct piv_object piv_objects[] = { "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x04", "\x30\x10", 0}, { PIV_OBJ_X509_PIV_AUTH, "X.509 Certificate for PIV Authentication", "2.16.840.1.101.3.7.2.1.1", 3, "\x5F\xC1\x05", "\x01\x01", PIV_OBJECT_TYPE_CERT} , - /* extra 400 is hack for MultOS card which returns 2200 bytes */ { PIV_OBJ_CHF, "Card Holder Fingerprints", "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x10", 0}, { PIV_OBJ_PI, "Printed Information", "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 0}, - { PIV_OBJ_CHFI, "Card Holder Facial Image", + { PIV_OBJ_CHFI, "Cardholder Facial Images", "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 0}, { PIV_OBJ_X509_DS, "X.509 Certificate for Digital Signature", "2.16.840.1.101.3.7.2.1.0", 3, "\x5F\xC1\x0A", "\x01\x00", PIV_OBJECT_TYPE_CERT}, @@ -149,6 +217,76 @@ static struct piv_object piv_objects[] = { "2.16.840.1.101.3.7.2.5.0", 3, "\x5F\xC1\x01", "\x05\x00", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_SEC_OBJ, "Security Object", "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 0}, + { PIV_OBJ_DISCOVERY, "Discovery Object", + "2.16.840.1.101.3.7.2.96.80", 1, "\x7E", "\x60\x50", 0}, + { PIV_OBJ_HISTORY, "Key History Object", + "2.16.840.1.101.3.7.2.96.96", 3, "\x5F\xC1\x0C", "\x60\x60", 0}, + +/* 800-73-3, 21 new objects, 20 history certificates */ + { PIV_OBJ_RETIRED_X509_1, "Retired X.509 Certificate for Key Management 1", + "2.16.840.1.101.3.7.2.16.1", 3, "\x5F\xC1\x0D", "\x10\x01", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_2, "Retired X.509 Certificate for Key Management 2", + "2.16.840.1.101.3.7.2.16.2", 3, "\x5F\xC1\x0E", "\x10\x02", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_3, "Retired X.509 Certificate for Key Management 3", + "2.16.840.1.101.3.7.2.16.3", 3, "\x5F\xC1\x0F", "\x10\x03", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_4, "Retired X.509 Certificate for Key Management 4", + "2.16.840.1.101.3.7.2.16.4", 3, "\x5F\xC1\x10", "\x10\x04", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_5, "Retired X.509 Certificate for Key Management 5", + "2.16.840.1.101.3.7.2.16.5", 3, "\x5F\xC1\x11", "\x10\x05", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_6, "Retired X.509 Certificate for Key Management 6", + "2.16.840.1.101.3.7.2.16.6", 3, "\x5F\xC1\x12", "\x10\x06", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_7, "Retired X.509 Certificate for Key Management 7", + "2.16.840.1.101.3.7.2.16.7", 3, "\x5F\xC1\x13", "\x10\x07", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_8, "Retired X.509 Certificate for Key Management 8", + "2.16.840.1.101.3.7.2.16.8", 3, "\x5F\xC1\x14", "\x10\x08", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_9, "Retired X.509 Certificate for Key Management 9", + "2.16.840.1.101.3.7.2.16.9", 3, "\x5F\xC1\x15", "\x10\x09", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_10, "Retired X.509 Certificate for Key Management 10", + "2.16.840.1.101.3.7.2.16.10", 3, "\x5F\xC1\x16", "\x10\x0A", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_11, "Retired X.509 Certificate for Key Management 11", + "2.16.840.1.101.3.7.2.16.11", 3, "\x5F\xC1\x17", "\x10\x0B", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_12, "Retired X.509 Certificate for Key Management 12", + "2.16.840.1.101.3.7.2.16.12", 3, "\x5F\xC1\x18", "\x10\x0C", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_13, "Retired X.509 Certificate for Key Management 13", + "2.16.840.1.101.3.7.2.16.13", 3, "\x5F\xC1\x19", "\x10\x0D", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_14, "Retired X.509 Certificate for Key Management 14", + "2.16.840.1.101.3.7.2.16.14", 3, "\x5F\xC1\x1A", "\x10\x0E", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_15, "Retired X.509 Certificate for Key Management 15", + "2.16.840.1.101.3.7.2.16.15", 3, "\x5F\xC1\x1B", "\x10\x0F", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_16, "Retired X.509 Certificate for Key Management 16", + "2.16.840.1.101.3.7.2.16.16", 3, "\x5F\xC1\x1C", "\x10\x10", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_17, "Retired X.509 Certificate for Key Management 17", + "2.16.840.1.101.3.7.2.16.17", 3, "\x5F\xC1\x1D", "\x10\x11", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_18, "Retired X.509 Certificate for Key Management 18", + "2.16.840.1.101.3.7.2.16.18", 3, "\x5F\xC1\x1E", "\x10\x12", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_19, "Retired X.509 Certificate for Key Management 19", + "2.16.840.1.101.3.7.2.16.19", 3, "\x5F\xC1\x1F", "\x10\x13", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + { PIV_OBJ_RETIRED_X509_20, "Retired X.509 Certificate for Key Management 20", + "2.16.840.1.101.3.7.2.16.20", 3, "\x5F\xC1\x20", "\x10\x14", + PIV_OBJECT_NOT_PRESENT|PIV_OBJECT_TYPE_CERT}, + + { PIV_OBJ_IRIS_IMAGE, "Cardholder Iris Images", + "2.16.840.1.101.3.7.2.16.21", 3, "\x5F\xC1\x21", "\x10\x15", 0}, + /* following not standard , to be used by piv-tool only for testing */ { PIV_OBJ_9B03, "3DES-ECB ADM", "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 0}, @@ -165,6 +303,48 @@ static struct piv_object piv_objects[] = { "2.16.840.1.101.3.7.2.9999.22", 2, "\x9D\x06", "\x9D\x06", PIV_OBJECT_TYPE_PUBKEY}, { PIV_OBJ_9E06, "Pub 9E key from last genkey", "2.16.840.1.101.3.7.2.9999.23", 2, "\x9E\x06", "\x9E\x06", PIV_OBJECT_TYPE_PUBKEY}, + + { PIV_OBJ_8206, "Pub 82 key ", + "2.16.840.1.101.3.7.2.9999.101", 2, "\x82\x06", "\x82\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8306, "Pub 83 key ", + "2.16.840.1.101.3.7.2.9999.102", 2, "\x83\x06", "\x83\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8406, "Pub 84 key ", + "2.16.840.1.101.3.7.2.9999.103", 2, "\x84\x06", "\x84\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8506, "Pub 85 key ", + "2.16.840.1.101.3.7.2.9999.104", 2, "\x85\x06", "\x85\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8606, "Pub 86 key ", + "2.16.840.1.101.3.7.2.9999.105", 2, "\x86\x06", "\x86\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8706, "Pub 87 key ", + "2.16.840.1.101.3.7.2.9999.106", 2, "\x87\x06", "\x87\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8806, "Pub 88 key ", + "2.16.840.1.101.3.7.2.9999.107", 2, "\x88\x06", "\x88\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8906, "Pub 89 key ", + "2.16.840.1.101.3.7.2.9999.108", 2, "\x89\x06", "\x89\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8A06, "Pub 8A key ", + "2.16.840.1.101.3.7.2.9999.109", 2, "\x8A\x06", "\x8A\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8B06, "Pub 8B key ", + "2.16.840.1.101.3.7.2.9999.110", 2, "\x8B\x06", "\x8B\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8C06, "Pub 8C key ", + "2.16.840.1.101.3.7.2.9999.111", 2, "\x8C\x06", "\x8C\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8D06, "Pub 8D key ", + "2.16.840.1.101.3.7.2.9999.112", 2, "\x8D\x06", "\x8D\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8E06, "Pub 8E key ", + "2.16.840.1.101.3.7.2.9999.113", 2, "\x8E\x06", "\x8E\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_8F06, "Pub 8F key ", + "2.16.840.1.101.3.7.2.9999.114", 2, "\x8F\x06", "\x8F\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9006, "Pub 90 key ", + "2.16.840.1.101.3.7.2.9999.115", 2, "\x90\x06", "\x90\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9106, "Pub 91 key ", + "2.16.840.1.101.3.7.2.9999.116", 2, "\x91\x06", "\x91\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9206, "Pub 92 key ", + "2.16.840.1.101.3.7.2.9999.117", 2, "\x92\x06", "\x92\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9306, "Pub 93 key ", + "2.16.840.1.101.3.7.2.9999.118", 2, "\x93\x06", "\x93\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9406, "Pub 94 key ", + "2.16.840.1.101.3.7.2.9999.119", 2, "\x94\x06", "\x94\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_9506, "Pub 95 key ", + "2.16.840.1.101.3.7.2.9999.120", 2, "\x95\x06", "\x95\x06", PIV_OBJECT_TYPE_PUBKEY}, + { PIV_OBJ_LAST_ENUM, "", "", 0, "", "", 0} }; @@ -177,6 +357,21 @@ static struct sc_card_driver piv_drv = { NULL, 0, NULL }; +static int piv_find_obj_by_containerid(sc_card_t *card, const u8 * str) +{ + int i; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "str=0x%02X%02X\n", str[0], str[1]); + + for (i = 0; piv_objects[i].enumtag < PIV_OBJ_LAST_ENUM; i++) { + if ( str[0] == piv_objects[i].containerid[0] + && str[1] == piv_objects[i].containerid[1]) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, i); + } + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, -1); +} + /* * If ptr == NULL, just return the size of the tag and lenght and data * otherwise, store tag and length at **ptr, and increment @@ -223,7 +418,7 @@ static size_t put_tag_and_len(unsigned int tag, size_t len, u8 **ptr) * Send a command and receive data. There is always something to send. * Used by GET DATA, PUT DATA, GENERAL AUTHENTICATE * and GENERATE ASYMMETRIC KEY PAIR. - * GET DATA may call to get the first 128 bytes to get the lenght gfrom the tag. + * GET DATA may call to get the first 128 bytes to get the lenght from the tag. * * A caller may provide a buffer, and length to read. If not provided, * an internal 4096 byte buffer is used, and a copy is returned to the @@ -252,7 +447,7 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, rbuf = rbufinitbuf; rbuflen = sizeof(rbufinitbuf); - /* if caller provided a buffer end length */ + /* if caller provided a buffer and length */ if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) { rbuf = *recvbuf; rbuflen = *recvbuflen; @@ -357,14 +552,15 @@ err: /* only do RSA for now */ static int piv_generate_key(sc_card_t *card, - struct sc_cardctl_cryptoflex_genkey_info *keydata) + sc_cardctl_piv_genkey_info_t *keydata) { int r; u8 *rbuf = NULL; size_t rbuflen = 0; size_t buf_len = 0; u8 *buf_end; - u8 *p, *rp, *tag; + u8 *p, *rp; + const u8 *tag; u8 tagbuf[16]; u8 outdata[3]; /* we could also add tag 81 for exponent */ size_t taglen, i; @@ -377,15 +573,16 @@ static int piv_generate_key(sc_card_t *card, keydata->exponent = 0; keydata->pubkey = NULL; keydata->pubkey_len = 0; - out_len = 3; outdata[0] = 0x80; outdata[1] = 0x01; - switch (keydata->key_bits) { - case 1024: outdata[2] = 0x06; break; - case 2048: outdata[2] = 0x07; break; - case 3072: outdata[2] = 0x05; break; + outdata[2] = keydata->key_algid; + switch (keydata->key_algid) { + case 0x05: keydata->key_bits = 3072; break; + case 0x06: keydata->key_bits = 1024; break; + case 0x07: keydata->key_bits = 2048; break; + /* TODO for EC, also set then curve parameter as the OID */ default: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_ARGUMENTS); } @@ -419,22 +616,29 @@ static int piv_generate_key(sc_card_t *card, goto err; } - tag = (u8 *) sc_asn1_find_tag(card->ctx, cp, in_len, 0x82, &taglen); - if (tag != NULL && taglen <= 4) { - keydata->exponent = 0; - for (i = 0; i < taglen;i++) { - keydata->exponent = (keydata->exponent<<8) + tag[i]; + /* if RSA vs EC */ + if (keydata->key_bits > 0 ) { + tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x82, &taglen); + if (tag != NULL && taglen <= 4) { + keydata->exponent = 0; + for (i = 0; i < taglen;i++) { + keydata->exponent = (keydata->exponent<<8) + tag[i]; + } } + tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x81, &taglen); + + if (tag != NULL && taglen > 0) { + keydata->pubkey = malloc(taglen); + if (keydata->pubkey == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + keydata->pubkey_len = taglen; + memcpy (keydata->pubkey, tag, taglen); + } + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Requires OpenSSL"); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } - tag = (u8 *) sc_asn1_find_tag(card->ctx, cp, in_len, 0x81, &taglen); - - if (tag != NULL && taglen > 0) { - keydata->pubkey = malloc(taglen); - if (keydata->pubkey == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - keydata->pubkey_len = taglen; - memcpy (keydata->pubkey, tag, taglen); - } + /* TODO could add key to cache so could use engine to generate key, and */ r = 0; } @@ -481,9 +685,9 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) sc_apdu_t apdu; u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; int r,i; - u8 *tag; + const u8 *tag; size_t taglen; - u8 *pix; + const u8 *pix; size_t pixlen; size_t resplen = sizeof(rbuf); @@ -498,9 +702,9 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, rbuf, &resplen); if (r >= 0 && resplen > 2 ) { - tag = (u8 *) sc_asn1_find_tag(card->ctx, rbuf, resplen, 0x61, &taglen); + tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, 0x61, &taglen); if (tag != NULL) { - pix = (u8 *) sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); + pix = sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); if (pix != NULL ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found PIX"); @@ -573,6 +777,71 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NO_CARD_SUPPORT); } +/* + * Read a DER encoded object from a file. Allocate and return the buf. + * Used to read the file defined in offCardCertURL from a cache. + * Also used for testing of History and Discovery objects from a file + * when testing with a card that does not support these new objects. + */ +static int piv_read_obj_from_file(sc_card_t * card, char * filename, + u8 **buf, size_t *buf_len) +{ + int r; + int f = -1; + size_t len; + u8 tagbuf[16]; + size_t rbuflen; + const u8 * body; + unsigned int cla_out, tag_out; + size_t bodylen; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + *buf = NULL; + *buf_len = 0; + f = open(filename, O_RDONLY); + if (f < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Unable to load PIV off card file: \"%s\"\n",filename); + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } + len = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ + if (len < 0) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Problem with \"%s\"\n",filename); + r = SC_ERROR_DATA_OBJECT_NOT_FOUND; + goto err; + } + body = tagbuf; + if (sc_asn1_read_tag(&body, 0xfffff, &cla_out, + &tag_out, &bodylen) != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DER problem\n"); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + rbuflen = body - tagbuf + bodylen; + *buf = malloc(rbuflen); + if (!*buf) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + memcpy(*buf, tagbuf, len); /* copy first or only part */ + if (rbuflen > len) { + len = read(f, *buf + sizeof(tagbuf), rbuflen - sizeof(tagbuf)); /* read rest */ + if (len != rbuflen - sizeof(tagbuf)) { + r = SC_ERROR_INVALID_ASN1_OBJECT; + free (*buf); + *buf = NULL; + goto err; + } + } + r = rbuflen; + *buf_len = rbuflen; +err: + if (f >= 0) + close(f); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} /* the tag is the PIV_OBJ_* */ static int piv_get_data(sc_card_t * card, int enumtag, @@ -583,6 +852,8 @@ static int piv_get_data(sc_card_t * card, int enumtag, u8 tagbuf[8]; size_t tag_len; const char * keyenvname = NULL; + const char * dataenvname = NULL; + char * filename = NULL; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "#%d \n", enumtag); @@ -605,9 +876,34 @@ static int piv_get_data(sc_card_t * card, int enumtag, * this is ugly, and maybe the pkcs15 cache would work * but we only need it to get the OpenSSL req with engine to work. * Each of the 4 keys with certs has its own file. + * Any other request for a pub key will return not found. */ switch (piv_objects[enumtag].enumtag) { +#ifdef PIV_TEST_WITH_OLD_CARDS + /* + * For testing 800-73-3 History and/or Discovery objects + * we can simulate reading them from a file and + * test with an older card. Since the older card + * does not have the keys, we can only do partial testing. + */ + + /* read them from a file */ + case PIV_OBJ_DISCOVERY: + dataenvname = "PIV_TEST_OBJ_DISCOVERY"; + break; + case PIV_OBJ_HISTORY: + dataenvname = "PIV_TEST_OBJ_HISTORY"; + +#endif + /* + * If we used the piv-tool to generate a key, + * we would have saved the public key as a file. + * This code is only used while signing a request + * After the certificate is loaded on the card, + * the public key is extracted from the certificate. + */ + case PIV_OBJ_9A06: keyenvname = "PIV_9A06_KEY"; break; @@ -620,9 +916,28 @@ static int piv_get_data(sc_card_t * card, int enumtag, case PIV_OBJ_9E06: keyenvname = "PIV_9E06_KEY"; break; + + default: + if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } } + if (dataenvname) { + filename = (char *)getenv(dataenvname); + if (filename) { + r = piv_read_obj_from_file(card, filename, buf, buf_len); + if (r == SC_ERROR_FILE_NOT_FOUND) /* ignore if not found */ + r = 0; + } + goto err; /* return error, or length */ + } + + if (keyenvname) { + /* This code is only used for card administration */ +#ifdef ENABLE_OPENSSL BIO * bp = NULL; RSA * rsa = NULL; u8 *q; @@ -681,7 +996,11 @@ static int piv_get_data(sc_card_t * card, int enumtag, r = *buf_len; - /* end of read PIV_OBJ_9A06 from file */ + /* end of read pub key from file */ +#else + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Requires OpenSSL"); + r = SC_ERROR_NOT_SUPPORTED; +#endif /* ENABLE_OPENSSL */ } else { if (*buf_len == 1 && *buf == NULL) { /* we need to get the length */ @@ -767,12 +1086,24 @@ static int piv_get_cached_data(sc_card_t * card, int enumtag, goto ok; } - /* not cached get it, piv_get_data will allocate a buf */ -sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get #%d", enumtag); + /* + * If we know it can not be on the card i.e. History object + * has been read, and we know what other certs may or + * may not be on the card. We can avoid extra overhead + */ + + if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"no_obj #%d", enumtag); + r = SC_ERROR_FILE_NOT_FOUND; + goto err; + } + + /* Not cached, try to get it, piv_get_data will allocate a buf */ + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get #%d", enumtag); rbuflen = 1; r = piv_get_data(card, enumtag, &rbuf, &rbuflen); if (r > 0) { - priv->obj_cache[enumtag].flags = PIV_OBJ_CACHE_VALID; + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_len = r; priv->obj_cache[enumtag].obj_data = rbuf; *buf = rbuf; @@ -787,7 +1118,7 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get #%d", enumtag); } else if (r == 0 || r == SC_ERROR_FILE_NOT_FOUND) { r = SC_ERROR_FILE_NOT_FOUND; - priv->obj_cache[enumtag].flags = PIV_OBJ_CACHE_VALID; + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_len = 0; } else if ( r < 0) { goto err; @@ -802,8 +1133,8 @@ err: static int piv_cache_internal_data(sc_card_t *card, int enumtag) { piv_private_data_t * priv = PIV_DATA(card); - u8* tag; - u8* body; + const u8* tag; + const u8* body; size_t taglen; size_t bodylen; int compressed = 0; @@ -816,7 +1147,7 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } - body = (u8 *) sc_asn1_find_tag(card->ctx, + body = sc_asn1_find_tag(card->ctx, priv->obj_cache[enumtag].obj_data, priv->obj_cache[enumtag].obj_len, 0x53, &bodylen); @@ -827,12 +1158,12 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) /* get the certificate out */ if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CERT) { - tag = (u8 *) sc_asn1_find_tag(card->ctx, body, bodylen, 0x71, &taglen); + tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x71, &taglen); /* 800-72-1 not clear if this is 80 or 01 Sent comment to NIST for 800-72-2 */ if (tag && (((*tag) & 0x80) || ((*tag) & 0x01))) { compressed = 1; } - tag = (u8 *) sc_asn1_find_tag(card->ctx, body, bodylen, 0x70, &taglen); + tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x70, &taglen); if (tag == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); @@ -863,7 +1194,7 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) /* convert pub key to internal */ } else if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { - tag = (u8 *) sc_asn1_find_tag(card->ctx, body, bodylen, *body, &taglen); + tag = sc_asn1_find_tag(card->ctx, body, bodylen, *body, &taglen); if (tag == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); @@ -900,7 +1231,7 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, int r; u8 *rbuf = NULL; size_t rbuflen = 0; - u8 *body; + const u8 *body; size_t bodylen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -917,12 +1248,13 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, if (r >=0) { /* an object wih no data will be considered not found */ - if (!rbuf || rbuf[0] == 0x00 || (rbuf[0] == 0x53 && rbuf[1] == 0x00)) { + /* Discovery tag = 0x73, all others are 0x53 */ + if (!rbuf || rbuf[0] == 0x00 || ((rbuf[0]&0xDF) == 0x53 && rbuf[1] == 0x00)) { r = SC_ERROR_FILE_NOT_FOUND; goto err; } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE rbuf=%p,rbuflen=%d,",rbuf, rbuflen); - body = (u8 *) sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x53, &bodylen); + body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, rbuf[0], &bodylen); if (body == NULL) { /* if missing, assume its the body */ /* DEE bug in the beta card */ @@ -1132,7 +1464,7 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, } /* if it worked, will cache it */ if (r >= 0 && priv->w_buf) { - priv->obj_cache[enumtag].flags = PIV_OBJ_CACHE_VALID; + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; priv->obj_cache[enumtag].obj_data = priv->w_buf; priv->obj_cache[enumtag].obj_len = priv->w_buf_len; } else { @@ -1212,6 +1544,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; +#ifdef ENABLE_OPENSSL int N; int locked = 0, outl, outl2; u8 *rbuf = NULL; @@ -1348,14 +1681,21 @@ err: if (rbuf) free(rbuf); +#else + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required"); + r = SC_ERROR_NOT_SUPPORTED; +#endif /* ENABLE_OPENSSL */ + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } +/* Currently only used for card administration */ static int piv_general_external_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { - /* unused: piv_private_data_t * priv = PIV_DATA(card); */ - int r, outl, outl2; + int r; +#ifdef ENABLE_OPENSSL + int outl, outl2; int N; int locked = 0; u8 *rbuf = NULL; @@ -1443,6 +1783,10 @@ err: sc_mem_clear(key, sizeof(key)); if (rbuf) free(rbuf); +#else + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"OpenSSL Required"); + r = SC_ERROR_NOT_SUPPORTED; +#endif /* ENABLE_OPENSSL */ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } @@ -1453,7 +1797,9 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri int i; u8 gbits; u8 *rbuf = NULL; - u8 *body, *fascn, *guid; + const u8 *body; + const u8 *fascn; + const u8 *guid; size_t rbuflen = 0, bodylen, fascnlen, guidlen; u8 temp[2000]; size_t templen = sizeof(temp); @@ -1475,10 +1821,10 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri r = SC_ERROR_INTERNAL; if (rbuflen != 0) { - body = (u8 *)sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x53, &bodylen); /* Pass the outer wrapper asn1 */ + body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x53, &bodylen); /* Pass the outer wrapper asn1 */ if (body != NULL && bodylen != 0) { - fascn = (u8 *)sc_asn1_find_tag(card->ctx, body, bodylen, 0x30, &fascnlen); /* Find the FASC-N data */ - guid = (u8 *)sc_asn1_find_tag(card->ctx, body, bodylen, 0x34, &guidlen); + fascn = sc_asn1_find_tag(card->ctx, body, bodylen, 0x30, &fascnlen); /* Find the FASC-N data */ + guid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x34, &guidlen); gbits = 0; /* if guid is valid, gbits will not be zero */ if (guid && guidlen == 16) { @@ -1510,14 +1856,56 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } +/* + * If the object can not be present on the card, because the History + * object is not present or the History object says its not present, + * return 1. If object may be present return 0. + * Cuts down on overhead, by not showing non existent objects to pkcs11 + * The path for the object is passed in and the first 2 bytes are used. + * Note: If the History or Discovery object is not found the + * PIV_OBJ_CACHE_NOT_PRESENT is set, as older cards do not have these. + * pkcs15-piv.c calls this via cardctl. + */ + +static int piv_is_object_present(sc_card_t *card, u8 *ptr) +{ + piv_private_data_t * priv = PIV_DATA(card); + int r = 0; + int enumtag; + + enumtag = piv_find_obj_by_containerid(card, ptr); + if (enumtag >= 0 && priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) + r = 1; + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +/* + * NIST 800-73-3 allows the default pin to be the PIV application 0x80 + * or the global pin for the card 0x00. Look at Discovery object to get this. + * called by pkcs15-piv.c via cardctl when setting up the pins. + */ +static int piv_get_pin_preference(sc_card_t *card, int *ptr) +{ + piv_private_data_t * priv = PIV_DATA(card); + + *ptr = priv->pin_preference; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS); +} + static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { + piv_private_data_t * priv = PIV_DATA(card); u8 * opts; /* A or M, key_ref, alg_id */ - - opts = (u8 *)ptr; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"cmd=%ld ptr=%p"); + if (priv == NULL) { + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); + } switch(cmd) { - case SC_CARDCTL_LIFECYCLE_SET: + case SC_CARDCTL_PIV_AUTHENTICATE: + opts = (u8 *)ptr; switch (*opts) { case 'A': return piv_general_external_authenticate(card, @@ -1529,16 +1917,22 @@ static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) break; } break; - case SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY: + case SC_CARDCTL_PIV_GENERATE_KEY: return piv_generate_key(card, - (struct sc_cardctl_cryptoflex_genkey_info *) ptr); + (sc_cardctl_piv_genkey_info_t *) ptr); break; case SC_CARDCTL_GET_SERIALNR: return piv_get_serial_nr_from_CHUI(card, (sc_serial_number_t *) ptr); break; + case SC_CARDCTL_PIV_PIN_PREFERENCE: + return piv_get_pin_preference(card, ptr); + break; + case SC_CARDCTL_PIV_OBJECT_PRESENT: + return piv_is_object_present(card, ptr); + break; } - return SC_ERROR_NOT_SUPPORTED; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED); } static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) @@ -1631,9 +2025,9 @@ static int piv_validate_general_authentication(sc_card_t *card, piv_private_data_t * priv = PIV_DATA(card); int r; u8 *p; - u8 *tag; + const u8 *tag; size_t taglen; - u8 *body; + const u8 *body; size_t bodylen; unsigned int real_alg_id; @@ -1674,10 +2068,10 @@ static int piv_validate_general_authentication(sc_card_t *card, sbuf, p - sbuf, &rbuf, &rbuflen); if ( r >= 0) { - body = (u8 *) sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7c, &bodylen); + body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7c, &bodylen); if (body) { - tag = (u8 *) sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &taglen); + tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &taglen); if (tag) { memcpy(out, tag, taglen); r = taglen; @@ -1709,23 +2103,6 @@ static int piv_decipher(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, piv_validate_general_authentication(card, data, datalen, out, outlen)); } - -static int piv_find_obj_by_containerid(sc_card_t *card, const u8 * str) -{ - int i; - - SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "str=0x%02X%02X\n", str[0], str[1]); - - for (i = 0; piv_objects[i].enumtag < PIV_OBJ_LAST_ENUM; i++) { - if ( str[0] == piv_objects[i].containerid[0] - && str[1] == piv_objects[i].containerid[1]) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, i); - } - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, -1); -} - - /* * the PIV-II does not always support files, but we will simulate * files and reading/writing using get/put_data @@ -1815,6 +2192,300 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, } +static int piv_process_discovery(sc_card_t *card) +{ + piv_private_data_t * priv = PIV_DATA(card); + int r; + u8 * rbuf = NULL; + size_t rbuflen = 0; + const u8 * body; + size_t bodylen; + const u8 * aid; + size_t aidlen; + const u8 * pinp; + size_t pinplen; + unsigned int cla_out, tag_out; + + + r = piv_get_cached_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); + if (r <= 0) { + priv->obj_cache[PIV_OBJ_DISCOVERY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + /* Discovery object is only object that has 3 byte Lc= 50017E + * and pree 800-73-3 cards may treat this as a strange error. + * So treat any error as not present + */ + r = 0; + goto err; + } + +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery = %p:%d",rbuf, rbuflen); + /* the object is now cached, see what we have */ + if (rbuflen != 0) { + body = rbuf; + if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem %d\n",r); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery 0x%2.2x 0x%2.2x %p:%d", + cla_out, tag_out, body, bodylen); + if ( cla_out+tag_out == 0x7E && body != NULL && bodylen != 0) { + aidlen = 0; + aid = sc_asn1_find_tag(card->ctx, body, bodylen, 0x4F, &aidlen); +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery aid=%p:%d",aid,aidlen); + if (aid == NULL || aidlen < piv_aids[0].len_short || + memcmp(aid,piv_aids[0].value,piv_aids[0].len_short) != 0) { /*TODO look at long */ + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Discovery object not PIV"); + r = SC_SUCCESS; /* not an error could be some other appl */ + goto err; + } + pinp = sc_asn1_find_tag(card->ctx, body, bodylen, 0x5F2F, &pinplen); +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery pinp=%p:%d",pinp,pinplen); + if (pinp && pinplen == 2) { +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery pinp flags=0x%2.2x 0x%2.2x",*pinp, *(pinp+1)); + r = SC_SUCCESS; + if (*pinp == 0x60 && *(pinp+1) == 0x20) { /* use Global pin */ + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Pin Preference - Global"); + priv->pin_preference = 0x00; + } + } + } + } + err: + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + +/* + * The history object lists what retired keys and certs are on the card + * or listed in the offCardCertURL. The user may have read the offCardURL file, + * ahead of time, and if so will use it for the certs listed. + * TODO: + * If the offCardCertURL is not cached by the user, should we wget it here? + * Its may be out of scope to have OpenSC read the URL. + */ + +static int piv_process_history(sc_card_t *card) +{ + piv_private_data_t * priv = PIV_DATA(card); + int r; + int i; + int enumtag; + u8 * rbuf = NULL; + size_t rbuflen = 0; + const u8 * body; + size_t bodylen; + const u8 * num; + size_t numlen; + const u8 * url = NULL; + size_t urllen; + u8 * ocfhfbuf = NULL; + unsigned int cla_out, tag_out; + size_t ocfhflen; + const u8 * seq; + const u8 * seqtag; + size_t seqlen; + const u8 * keyref; + size_t keyreflen; + const u8 * cert; + size_t certlen; + size_t certobjlen, i2; + u8 * certobj; + u8 * cp; + + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + r = piv_get_cached_data(card, PIV_OBJ_HISTORY, &rbuf, &rbuflen); + if (r == SC_ERROR_FILE_NOT_FOUND) + r = 0; /* OK if not found */ + if (r <= 0) { + priv->obj_cache[PIV_OBJ_HISTORY].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + goto err; /* no file, must be pre 800-73-3 card and not on card */ + } + + /* the object is now cached, see what we have */ + if (rbuflen != 0) { + body = rbuf; + if ((r = sc_asn1_read_tag(&body, rbuflen, &cla_out, &tag_out, &bodylen)) != SC_SUCCESS) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem %d\n",r); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + + if ( cla_out+tag_out == 0x53 && body != NULL && bodylen != 0) { + numlen = 0; + num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC1, &numlen); + if (num) { + if (numlen != 1 || + *num > PIV_OBJ_RETIRED_X509_20-PIV_OBJ_RETIRED_X509_1+1) { + r = SC_ERROR_INTERNAL; /* TODO some other error */ + goto err; + } + priv->keysWithOnCardCerts = *num; + } + + numlen = 0; + num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC2, &numlen); + if (num) { + if (numlen != 1 || + *num > PIV_OBJ_RETIRED_X509_20-PIV_OBJ_RETIRED_X509_1+1) { + r = SC_ERROR_INTERNAL; /* TODO some other error */ + goto err; + } + priv->keysWithOffCardCerts = *num; + } + + url = sc_asn1_find_tag(card->ctx, body, bodylen, 0xF3, &urllen); + if (url) { + priv->offCardCertURL = calloc(1,urllen+1); + if (priv->offCardCertURL == NULL) + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); + memcpy(priv->offCardCertURL, url, urllen); + } + } else { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Problem with Histroy object\n"); + goto err; + } + } + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "History on=%d off=%d URL=%s\n", + priv->keysWithOnCardCerts, priv->keysWithOffCardCerts, + priv->offCardCertURL ? priv->offCardCertURL:"NONE"); + + /* now mark what objects are on the card */ + for (i=0; ikeysWithOnCardCerts; i++) { + priv->obj_cache[PIV_OBJ_RETIRED_X509_1+i].flags &= ~PIV_OBJ_CACHE_NOT_PRESENT; + } + + /* + * If user has gotten copy of the file from the offCardCertsURL, + * we will read in and add the certs to the cache as listed on + * the card. some of the certs may be on the card as well. + * + * Get file name from url. verify that the filename is valid + * The URL ends in a SHA1 string. We will use this as the filename + * in the directory used for the PKCS15 cache + */ + + r = 0; + if (priv->offCardCertURL) { + char * fp; + char filename[PATH_MAX]; + + if (strncmp("http://", priv->offCardCertURL, 7)) { + r = SC_ERROR_INVALID_DATA; + goto err; + } + /* find the last / so we have the filename part */ + fp = strrchr(priv->offCardCertURL + 7,'/'); + if (fp == NULL) { + r = SC_ERROR_INVALID_DATA; + goto err; + } + fp++; + + /* Use the same directory as used for other OpenSC cached items */ + r = sc_get_cache_dir(card->ctx, filename, + sizeof(filename) - strlen(fp) - 2); + if (r != SC_SUCCESS) + goto err; +#ifdef _WIN32 + strcat(filename,"\\"); +#else + strcat(filename,"/"); +#endif + strcat(filename,fp); + + r = piv_read_obj_from_file(card, filename, + &ocfhfbuf, &ocfhflen); + if (r == SC_ERROR_FILE_NOT_FOUND) { + r = 0; + goto err; + } + + /* + * Its a seq of seq of a key ref and cert + */ + + body = ocfhfbuf; + if (sc_asn1_read_tag(&body, ocfhflen, &cla_out, + &tag_out, &bodylen) != SC_SUCCESS || + cla_out+tag_out != 0x30) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DER problem\n"); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + seq = body; + while (bodylen > 0) { + seqtag = seq; + if (sc_asn1_read_tag(&seq, bodylen, &cla_out, + &tag_out, &seqlen) != SC_SUCCESS || + cla_out+tag_out != 0x30) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem\n"); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + keyref = sc_asn1_find_tag(card->ctx, + seq, seqlen, 0x04, &keyreflen); + if (!keyref || keyreflen != 1 || + (*keyref < 0x82 && *keyref > 0x95)) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DER problem\n"); + r = SC_ERROR_INVALID_ASN1_OBJECT; + goto err; + } + cert = keyref + keyreflen; + certlen = seqlen - (cert - seq); + + enumtag = PIV_OBJ_RETIRED_X509_1 + *keyref - 0x82; + /* now add the cert like another object */ + + i2 = put_tag_and_len(0x70,certlen, NULL) + + put_tag_and_len(0x71, 1, NULL) + + put_tag_and_len(0xFE, 0, NULL); + + certobjlen = put_tag_and_len(0x53, i2, NULL); + + certobj = malloc(certobjlen); + if (certobj == NULL) { + r = SC_ERROR_OUT_OF_MEMORY; + goto err; + } + cp = certobj; + put_tag_and_len(0x53, i2, &cp); + put_tag_and_len(0x70,certlen, &cp); + memcpy(cp, cert, certlen); + cp += certlen; + put_tag_and_len(0x71, 1,&cp); + *cp++ = 0x00; + put_tag_and_len(0xFE, 0, &cp); + + priv->obj_cache[enumtag].obj_data = certobj; + priv->obj_cache[enumtag].obj_len = certobjlen; + priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; + priv->obj_cache[enumtag].flags &= ~PIV_OBJ_CACHE_NOT_PRESENT; + + r = piv_cache_internal_data(card, enumtag); + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "got internal r=%d\n",r); + + certobj = NULL; + certobjlen = 0; + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, + "Added from off card file #%d %p:%d 0x%02X \n", + enumtag, + priv->obj_cache[enumtag].obj_data, + priv->obj_cache[enumtag].obj_len, *keyref); + + bodylen -= (seqlen + seq - seqtag); + seq += seqlen; + } + } +err: + if (ocfhfbuf) + free(ocfhfbuf); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); +} + static int piv_finish(sc_card_t *card) { @@ -1827,8 +2498,11 @@ static int piv_finish(sc_card_t *card) sc_file_free(priv->aid_file); if (priv->w_buf) free(priv->w_buf); + if (priv->offCardCertURL) + free(priv->offCardCertURL); for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) { -sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE freeing #%d, %p:%d %p:%d", i, + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE freeing #%d, 0x%02x %p:%d %p:%d", i, + priv->obj_cache[i].flags, priv->obj_cache[i].obj_data, priv->obj_cache[i].obj_len, priv->obj_cache[i].internal_obj_data, priv->obj_cache[i].internal_obj_len); if (priv->obj_cache[i].obj_data) @@ -1860,7 +2534,7 @@ static int piv_match_card(sc_card_t *card) static int piv_init(sc_card_t *card) { - int r; + int r, i; unsigned long flags; piv_private_data_t *priv; @@ -1871,6 +2545,13 @@ static int piv_init(sc_card_t *card) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); priv->aid_file = sc_file_new(); priv->selected_obj = -1; + priv->pin_preference = 0x80; /* 800-73-3 part 1, table 3 */ + + /* Some objects will only be present if Histroy object says so */ + for (i=0; i < PIV_OBJ_LAST_ENUM -1; i++) { + if(piv_objects[i].flags & PIV_OBJECT_NOT_PRESENT) + priv->obj_cache[i].flags |= PIV_OBJ_CACHE_NOT_PRESENT; + } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Max send = %d recv = %d\n", card->max_send_size, card->max_recv_size); @@ -1894,6 +2575,15 @@ static int piv_init(sc_card_t *card) card->caps |= SC_CARD_CAP_RNG; + /* + * 800-73-3 cards may have a history object and/or a discovery object + * We want to process them now as this has information on what + * keys and certs the card has and how the pin might be used. + */ + r = piv_process_history(card); + + r = piv_process_discovery(card); + if (r > 0) r = 0; SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); @@ -1954,4 +2644,3 @@ struct sc_card_driver * sc_get_piv_driver(void) } #endif -#endif /* ENABLE_OPENSSL */ diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index ad84b39b..ad4f4d7a 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -195,6 +195,15 @@ enum { SC_CARDCTL_MYEID_GETDATA, SC_CARDCTL_MYEID_GENERATE_STORE_KEY, SC_CARDCTL_MYEID_ACTIVATE_CARD, + + /* + * PIV specific calls + */ + SC_CARDCTL_PIV_BASE = _CTL_PREFIX('P', 'I', 'V'), + SC_CARDCTL_PIV_AUTHENTICATE, + SC_CARDCTL_PIV_GENERATE_KEY, + SC_CARDCTL_PIV_PIN_PREFERENCE, + SC_CARDCTL_PIV_OBJECT_PRESENT, }; enum { @@ -736,6 +745,18 @@ typedef struct sc_rtecp_genkey_data { unsigned char *invq; }; +/* + * PIV info + */ +typedef struct sc_cardctl_piv_genkey_info_st { + unsigned int key_num; + unsigned int key_algid; /* RSA 5, 6, 7; EC 11, 14 */ + unsigned int key_bits; /* RSA */ + unsigned long exponent; /* RSA */ + unsigned char * pubkey; /* RSA */ + unsigned int pubkey_len; /* RSA */ +} sc_cardctl_piv_genkey_info_t; + #ifdef __cplusplus } #endif diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 75e81f5a..6a060d65 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -85,9 +85,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "setcos", (void *(*)(void)) sc_get_setcos_driver }, { "muscle", (void *(*)(void)) sc_get_muscle_driver }, { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, -#ifdef ENABLE_OPENSSL { "PIV-II", (void *(*)(void)) sc_get_piv_driver }, -#endif { "itacns", (void *(*)(void)) sc_get_itacns_driver }, /* javacard without supported applet - last before default */ { "javacard", (void *(*)(void)) sc_get_javacard_driver }, diff --git a/src/libopensc/pkcs15-piv.c b/src/libopensc/pkcs15-piv.c index 46019d26..8eef4f89 100644 --- a/src/libopensc/pkcs15-piv.c +++ b/src/libopensc/pkcs15-piv.c @@ -2,7 +2,8 @@ * partial PKCS15 emulation for PIV-II cards * only minimal use of the authentication cert and key * - * Copyright (C) 2005,2006,2007,2008,2009 Douglas E. Engert + * Copyright (C) 2005,2006,2007,2008,2009,2010 + * Douglas E. Engert * 2004, Nils Larsch * Copyright (C) 2006, Identity Alliance, * Thomas Harning @@ -54,7 +55,6 @@ typedef struct cdata_st { int authority; const char *path; int obj_flags; - int found; } cdata; typedef struct pdata_st { @@ -68,26 +68,23 @@ typedef struct pdata_st { unsigned int storedlen; int flags; int tries_left; - const char pad_char; + const unsigned char pad_char; int obj_flags; } pindata; typedef struct pubdata_st { const char *id; const char *label; - unsigned int modulus_len; int usage; const char *path; int ref; const char *auth_id; int obj_flags; - int found; } pubdata; typedef struct prdata_st { const char *id; const char *label; - unsigned int modulus_len; int usage; const char *path; int ref; @@ -95,6 +92,13 @@ typedef struct prdata_st { int obj_flags; } prdata; +typedef struct common_key_info_st { + int cert_found; + int pubkey_found; + int key_alg; + unsigned int modulus_len; + int not_present; +} common_key_info; static int piv_detect_card(sc_pkcs15_card_t *p15card) { @@ -111,7 +115,9 @@ static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card) { /* The cert objects will return all the data */ -const objdata objects[] = { + /* Note: pkcs11 objects do not have CK_ID values */ + + static const objdata objects[] = { {"1", "Card Capability Container", "2.16.840.1.101.3.7.1.219.0", NULL, "DB00", 0}, {"2", "Card Holder Unique Identifier", @@ -120,11 +126,11 @@ const objdata objects[] = { "2.16.840.1.101.3.7.2.48.2", NULL, "3010", 0}, {"4", "X.509 Certificate for PIV Authentication", "2.16.840.1.101.3.7.2.1.1", NULL, "0101", 0}, - {"5", "Card Holder Fingerprints", + {"5", "Cardholder Fingerprints", "2.16.840.1.101.3.7.2.96.16", "1", "6010", SC_PKCS15_CO_FLAG_PRIVATE}, {"6", "Printed Information", "2.16.840.1.101.3.7.2.48.1", "1", "3001", SC_PKCS15_CO_FLAG_PRIVATE}, - {"7", "Card Holder Facial Image", + {"7", "Cardholder Facial Image", "2.16.840.1.101.3.7.2.96.48", "1", "6030", SC_PKCS15_CO_FLAG_PRIVATE}, {"8", "X.509 Certificate for Digital Signature", "2.16.840.1.101.3.7.2.1.0", NULL, "0100", 0}, @@ -134,27 +140,95 @@ const objdata objects[] = { "2.16.840.1.101.3.7.2.5.0", NULL, "0500", 0}, {"11", "Security Object", "2.16.840.1.101.3.7.2.144.0", NULL, "9000", 0}, + {"12", "Discovery Object", + "2.16.840.1.101.3.7.2.96.80", NULL, "6050", 0}, + {"13", "Key History Object", + "2.16.840.1.101.3.7.2.96.96", NULL, "6060", 0}, + {"14", "Cardholder Iris Image", + "2.16.840.1.101.3.7.2.16.21", NULL, "1015", SC_PKCS15_CO_FLAG_PRIVATE}, + + {"15", "Retired X.509 Certificate for Key Management 1", + "2.16.840.1.101.3.7.2.16.1", NULL, "1001", 0}, + {"16", "Retired X.509 Certificate for Key Management 2", + "2.16.840.1.101.3.7.2.16.2", NULL, "1002", 0}, + {"17", "Retired X.509 Certificate for Key Management 3", + "2.16.840.1.101.3.7.2.16.3", NULL, "1003", 0}, + {"18", "Retired X.509 Certificate for Key Management 4", + "2.16.840.1.101.3.7.2.16.4", NULL, "1004", 0}, + {"19", "Retired X.509 Certificate for Key Management 5", + "2.16.840.1.101.3.7.2.16.5", NULL, "1005", 0}, + {"20", "Retired X.509 Certificate for Key Management 6", + "2.16.840.1.101.3.7.2.16.6", NULL, "1006", 0}, + {"21", "Retired X.509 Certificate for Key Management 7", + "2.16.840.1.101.3.7.2.16.7", NULL, "1007", 0}, + {"22", "Retired X.509 Certificate for Key Management 8", + "2.16.840.1.101.3.7.2.16.8", NULL, "1008", 0}, + {"23", "Retired X.509 Certificate for Key Management 9", + "2.16.840.1.101.3.7.2.16.9", NULL, "1009", 0}, + {"24", "Retired X.509 Certificate for Key Management 10", + "2.16.840.1.101.3.7.2.16.10", NULL, "100A", 0}, + {"25", "Retired X.509 Certificate for Key Management 11", + "2.16.840.1.101.3.7.2.16.11", NULL, "100B", 0}, + {"26", "Retired X.509 Certificate for Key Management 12", + "2.16.840.1.101.3.7.2.16.12", NULL, "100C", 0}, + {"27", "Retired X.509 Certificate for Key Management 13", + "2.16.840.1.101.3.7.2.16.13", NULL, "100D", 0}, + {"28", "Retired X.509 Certificate for Key Management 14", + "2.16.840.1.101.3.7.2.16.14", NULL, "100E", 0}, + {"29", "Retired X.509 Certificate for Key Management 15", + "2.16.840.1.101.3.7.2.16.15", NULL, "100F", 0}, + {"30", "Retired X.509 Certificate for Key Management 16", + "2.16.840.1.101.3.7.2.16.16", NULL, "1010", 0}, + {"31", "Retired X.509 Certificate for Key Management 17", + "2.16.840.1.101.3.7.2.16.17", NULL, "1011", 0}, + {"32", "Retired X.509 Certificate for Key Management 18", + "2.16.840.1.101.3.7.2.16.18", NULL, "1012", 0}, + {"33", "Retired X.509 Certificate for Key Management 19", + "2.16.840.1.101.3.7.2.16.19", NULL, "1013", 0}, + {"34", "Retired X.509 Certificate for Key Management 20", + "2.16.840.1.101.3.7.2.16.20", NULL, "1014", 0}, {NULL, NULL, NULL, NULL, NULL, 0} }; /* - * NIST 800-73-1 is proposing to lift the restriction on - * requering pin protected certs. Thus the default will be to - * not require this. But there are a number of test cards - * that do enforce it. Code later on will allow SC_PKCS15_CO_FLAG_PRIVATE - * to be set. + * NIST 800-73-1 lifted the restriction on + * requering pin protected certs. Thus the default is to + * not require this. */ /* certs will be pulled out from the cert objects */ - cdata certs[] = { - {"1", "Certificate for PIV Authentication", 0, "0101cece", 0, 0}, + /* the number of cert, pubkey and prkey triplets */ - {"2", "Certificate for Digital Signature", 0, "0100cece", 0, 0}, - {"3", "Certificate for Key Management", 0, "0102cece", 0, 0}, - {"4", "Certificate for Card Authentication", 0, "0500cece", 0, 0}, - {NULL, NULL, 0, NULL, 0, 0} +#define PIV_NUM_CERTS_AND_KEYS 24 + + static const cdata certs[PIV_NUM_CERTS_AND_KEYS] = { + {"1", "Certificate for PIV Authentication", 0, "0101cece", 0}, + {"2", "Certificate for Digital Signature", 0, "0100cece", 0}, + {"3", "Certificate for Key Management", 0, "0102cece", 0}, + {"4", "Certificate for Card Authentication", 0, "0500cece", 0}, + {"5", "Retired Certificate for Key Management 1", 0, "1001cece", 0}, + {"6", "Retired Certificate for Key Management 2", 0, "1002cece", 0}, + {"7", "Retired Certificate for Key Management 3", 0, "1003cece", 0}, + {"8", "Retired Certificate for Key Management 4", 0, "1004cece", 0}, + {"9", "Retired Certificate for Key Management 5", 0, "1005cece", 0}, + {"10", "Retired Certificate for Key Management 6", 0, "1006cece", 0}, + {"11", "Retired Certificate for Key Management 7", 0, "1007cece", 0}, + {"12", "Retired Certificate for Key Management 8", 0, "1008cece", 0}, + {"13", "Retired Certificate for Key Management 9", 0, "1009cece", 0}, + {"14", "Retired Certificate for Key Management 10", 0, "100Acece", 0}, + {"15", "Retired Certificate for Key Management 11", 0, "100Bcece", 0}, + {"16", "Retired Certificate for Key Management 12", 0, "100Ccece", 0}, + {"17", "Retired Certificate for Key Management 13", 0, "100Dcece", 0}, + {"18", "Retired Certificate for Key Management 14", 0, "100Ecece", 0}, + {"19", "Retired Certificate for Key Management 15", 0, "100Fcece", 0}, + {"20", "Retired Certificate for Key Management 16", 0, "1010cece", 0}, + {"21", "Retired Certificate for Key Management 17", 0, "1011cece", 0}, + {"22", "Retired Certificate for Key Management 18", 0, "1012cece", 0}, + {"23", "Retired Certificate for Key Management 19", 0, "1013cece", 0}, + {"24", "Retired Certificate for Key Management 20", 0, "1014cece", 0} }; - const pindata pins[] = { + static const pindata pins[] = { { "1", "PIV Card Holder pin", "", 0x80, + /* label and ref will change if using global pin */ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 8, 4, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | @@ -169,8 +243,6 @@ const objdata objects[] = { SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN, -1, 0xFF, SC_PKCS15_CO_FLAG_PRIVATE }, - /* there are some more key, but dont need for now */ - /* The admin 9b might fall in here */ { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0} }; @@ -181,60 +253,180 @@ const objdata objects[] = { * the cert, pubkey and privkey are a set. * Key usages bits taken from pkcs15v1_1 Table 2 */ - pubdata pubkeys[] = { + static const pubdata pubkeys[PIV_NUM_CERTS_AND_KEYS] = { - { "1", "PIV AUTH pubkey", 0000, + { "1", "PIV AUTH pubkey", SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, - "9A06", 0x9A, "1", 0, 0}, - { "2", "SIGN pubkey", 0000, + "9A06", 0x9A, "1", 0}, + { "2", "SIGN pubkey", SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, - "9C06", 0x9C, "1", 0, 0}, - { "3", "KEY MAN pubkey", 0000, + "9C06", 0x9C, "1", 0}, + { "3", "KEY MAN pubkey", SC_PKCS15_PRKEY_USAGE_WRAP, - "9D06", 0x9D, "1", 0, 0}, - { "4", "CARD AUTH pubkey", 0000, + "9D06", 0x9D, "1", 0}, + { "4", "CARD AUTH pubkey", SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, - "9E06", 0x9E, "0", 0, 0}, /* no pin, and avail in contactless */ - { NULL, NULL, 0, 0, NULL, 0, NULL, 0, 0} - + "9E06", 0x9E, "0", 0}, /* no pin, and avail in contactless */ + + { "5", "Retired KEY MAN 1", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8206", 0x82, "1", 0}, + { "6", "Retired KEY MAN 2", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8306", 0x83, "1", 0}, + { "7", "Retired KEY MAN 3", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8406", 0x84, "1", 0}, + { "8", "Retired KEY MAN 4", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8506", 0x85, "1", 0}, + { "9", "Retired KEY MAN 5", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8606", 0x86, "1", 0}, + { "10", "Retired KEY MAN 6", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8706", 0x87, "1", 0}, + { "11", "Retired KEY MAN 7", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8806", 0x88, "1", 0}, + { "12", "Retired KEY MAN 8", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8906", 0x89, "1", 0}, + { "13", "Retired KEY MAN 9", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8A06", 0x8A, "1", 0}, + { "14", "Retired KEY MAN 10", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8B06", 0x8B, "1", 0}, + { "15", "Retired KEY MAN 11", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8C06", 0x8C, "1", 0}, + { "16", "Retired KEY MAN 12", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8D06", 0x8D, "1", 0}, + { "17", "Retired KEY MAN 13", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8E06", 0x8E, "1", 0}, + { "18", "Retired KEY MAN 14", + SC_PKCS15_PRKEY_USAGE_WRAP, + "8F06", 0x8F, "1", 0}, + { "19", "Retired KEY MAN 15", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9006", 0x90, "1", 0}, + { "20", "Retired KEY MAN 16", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9106", 0x91, "1", 0}, + { "21", "Retired KEY MAN 17", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9206", 0x92, "1", 0}, + { "22", "Retired KEY MAN 18", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9306", 0x93, "1", 0}, + { "23", "Retired KEY MAN 19", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9406", 0x94, "1", 0}, + { "24", "Retired KEY MAN 20", + SC_PKCS15_PRKEY_USAGE_WRAP, + "9506", 0x95, "1", 0} }; - prdata prkeys[] = { - { "1", "PIV AUTH key", 0000, + static const prdata prkeys[PIV_NUM_CERTS_AND_KEYS] = { + { "1", "PIV AUTH key", SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER, "", 0x9A, "1", 0}, - { "2", "SIGN key", 0000, + { "2", "SIGN key", SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, "", 0x9C, "1", 0}, - { "3", "KEY MAN key", 0000, + { "3", "KEY MAN key", SC_PKCS15_PRKEY_USAGE_UNWRAP, "", 0x9D, "1", 0}, - { "4", "CARD AUTH key", 0000, + { "4", "CARD AUTH key", SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER, "", 0x9E, NULL, 0}, /* no PIN needed, works with wireless */ - { NULL, NULL, 0, 0, NULL, 0, NULL, 0} + { "5", "Retired KEY MAN 1", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x82, "1", 0}, + { "6", "Retired KEY MAN 2", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x83, "1", 0}, + { "7", "Retired KEY MAN 3", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x84, "1", 0}, + { "8", "Retired KEY MAN 4", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x85, "1", 0}, + { "9", "Retired KEY MAN 5", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x86, "1", 0}, + { "10", "Retired KEY MAN 6", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x87, "1", 0}, + { "11", "Retired KEY MAN 7", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x88, "1", 0}, + { "12", "Retired KEY MAN 8", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x89, "1", 0}, + { "13", "Retired KEY MAN 9", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8A, "1", 0}, + { "14", "Retired KEY MAN 10", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8B, "1", 0}, + { "15", "Retired KEY MAN 11", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8C, "1", 0}, + { "16", "Retired KEY MAN 12", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8D, "1", 0}, + { "17", "Retired KEY MAN 13", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8E, "1", 0}, + { "18", "Retired KEY MAN 14", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x8F, "1", 0}, + { "19", "Retired KEY MAN 15", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x90, "1", 0}, + { "20", "Retired KEY MAN 16", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x91, "1", 0}, + { "21", "Retired KEY MAN 17", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x92, "1", 0}, + { "22", "Retired KEY MAN 18", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x93, "1", 0}, + { "23", "Retired KEY MAN 19", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x94, "1", 0}, + { "24", "Retired KEY MAN 20", + SC_PKCS15_PRKEY_USAGE_UNWRAP, + "", 0x95, "1", 0} }; int r, i; sc_card_t *card = p15card->card; sc_file_t *file_out = NULL; - int exposed_cert[4] = {1, 0, 0, 0}; + int exposed_cert[PIV_NUM_CERTS_AND_KEYS] = {1, 0, 0, 0}; sc_serial_number_t serial; char buf[SC_MAX_SERIALNR * 2 + 1]; + common_key_info ckis[PIV_NUM_CERTS_AND_KEYS]; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); @@ -272,12 +464,11 @@ const objdata objects[] = { sc_pkcs15_format_id(objects[i].id, &obj_info.id); sc_format_path(objects[i].path, &obj_info.path); - /* We could make sure the object is on the card */ - /* But really don't need to do this now */ -/* r = sc_select_file(card, &obj_info.path, NULL); - if (r == SC_ERROR_FILE_NOT_FOUND) - continue; -*/ + /* See if the object can not be present on the card */ + r = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &obj_info.path); + if (r == 1) + continue; /* Not on card, do not define the object */ + strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); r = sc_format_oid(&obj_info.app_oid, objects[i].aoid); if (r != SC_SUCCESS) @@ -307,12 +498,17 @@ const objdata objects[] = { */ /* set certs */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding certs..."); - for (i = 0; certs[i].label; i++) { + for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { struct sc_pkcs15_cert_info cert_info; struct sc_pkcs15_object cert_obj; sc_pkcs15_der_t cert_der; sc_pkcs15_cert_t *cert_out; + ckis[i].cert_found = 0; + ckis[i].key_alg = -1; + ckis[i].pubkey_found = 0; + ckis[i].modulus_len = 0; + if ((card->flags & 0x20) && (exposed_cert[i] == 0)) continue; @@ -326,9 +522,14 @@ const objdata objects[] = { strncpy(cert_obj.label, certs[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); cert_obj.flags = certs[i].obj_flags; - /* see if we have a cert */ + /* See if the cert might be present or not. */ + r = (card->ops->card_ctl)(card, SC_CARDCTL_PIV_OBJECT_PRESENT, &cert_info.path); + if (r == 1) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Cert can not be present,i=%d", i); + continue; + } - /* use a &file_out so card-piv will read cert if present */ + /* use a &file_out so card-piv.c will read cert if present */ r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len, &file_out); if (file_out) { @@ -341,7 +542,7 @@ const objdata objects[] = { continue; } - certs[i].found = 1; + ckis[i].cert_found = 1; /* cache it using the PKCS15 emulation objects */ /* as it does not change */ if (cert_der.value) { @@ -355,11 +556,16 @@ const objdata objects[] = { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r); continue; } - /* TODO support DSA keys */ + /* TODO support EC keys */ + ckis[i].key_alg = cert_out->key.algorithm; if (cert_out->key.algorithm == SC_ALGORITHM_RSA) { - /* save modulus_len in pub and priv */ - pubkeys[i].modulus_len = cert_out->key.u.rsa.modulus.len * 8; - prkeys[i].modulus_len = cert_out->key.u.rsa.modulus.len * 8; + /* save modulus_len for pub and priv */ + ckis[i].modulus_len = cert_out->key.u.rsa.modulus.len * 8; + } else { +/*TODO add the SC_ALGORITHM_EC */ + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Unsuported key.algorithm %d", cert_out->key.algorithm); + ckis[i].modulus_len = 1024; /* set some value for now */ } sc_pkcs15_free_certificate(cert_out); @@ -375,6 +581,8 @@ const objdata objects[] = { for (i = 0; pins[i].label; i++) { struct sc_pkcs15_pin_info pin_info; struct sc_pkcs15_object pin_obj; + const char * label; + int pin_ref; memset(&pin_info, 0, sizeof(pin_info)); memset(&pin_obj, 0, sizeof(pin_obj)); @@ -390,7 +598,16 @@ const objdata objects[] = { sc_format_path(pins[i].path, &pin_info.path); pin_info.tries_left = -1; - strncpy(pin_obj.label, pins[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); + label = pins[i].label; + if (i == 0 && + (card->ops->card_ctl)(card, SC_CARDCTL_PIV_PIN_PREFERENCE, + &pin_ref) == 0 && + pin_ref == 0x00) { /* must be 80 for PIV pin, or 00 for Global PIN */ + pin_info.reference = pin_ref; + label = "Global PIN"; + } +sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE Adding pin %d label=%s",i, label); + strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1); pin_obj.flags = pins[i].obj_flags; r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); @@ -406,7 +623,7 @@ const objdata objects[] = { * at a later time. The piv-tool can stach in file */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding pub keys..."); - for (i = 0; pubkeys[i].label; i++) { + for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { struct sc_pkcs15_pubkey_info pubkey_info; struct sc_pkcs15_object pubkey_obj; struct sc_pkcs15_pubkey *p15_key; @@ -433,9 +650,9 @@ const objdata objects[] = { if (pubkeys[i].auth_id) sc_pkcs15_format_id(pubkeys[i].auth_id, &pubkey_obj.auth_id); - if (certs[i].found == 0) { /* no cert found */ + if (ckis[i].cert_found == 0) { /* no cert found */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"No cert for this pub key i=%d",i); - /* TODO DSA */ + /* TODO EC */ pubkey_obj.type = SC_PKCS15_TYPE_PUBKEY_RSA; pubkey_obj.data = &pubkey_info; r = sc_pkcs15_read_pubkey(p15card, &pubkey_obj, &p15_key); @@ -450,27 +667,34 @@ const objdata objects[] = { */ if (p15_key->algorithm == SC_ALGORITHM_RSA) { /* save modulus_len in pub and priv */ - pubkeys[i].modulus_len = p15_key->u.rsa.modulus.len * 8; - prkeys[i].modulus_len = p15_key->u.rsa.modulus.len * 8; - pubkeys[i].found = 1; + ckis[i].key_alg = SC_ALGORITHM_RSA; + ckis[i].modulus_len = p15_key->u.rsa.modulus.len * 8; + ckis[i].pubkey_found = 1; } } - pubkey_info.modulus_length = pubkeys[i].modulus_len; + /* TODO need to support EC */ + if (ckis[i].key_alg != SC_ALGORITHM_RSA) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"key_alg for %d not RSA %d",i, ckis[i].key_alg); + continue; + } + + pubkey_info.modulus_length = ckis[i].modulus_len; strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); - /* TODO DSA keys */ + /* TODO EC keys */ + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"adding pubkey for %d keyalg=%d",i, ckis[i].key_alg); r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */ - pubkeys[i].found = 1; + ckis[i].pubkey_found = 1; } /* set private keys */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "PIV-II adding private keys..."); - for (i = 0; prkeys[i].label; i++) { + for (i = 0; i < PIV_NUM_CERTS_AND_KEYS; i++) { struct sc_pkcs15_prkey_info prkey_info; struct sc_pkcs15_object prkey_obj; @@ -480,16 +704,16 @@ const objdata objects[] = { memset(&prkey_info, 0, sizeof(prkey_info)); memset(&prkey_obj, 0, sizeof(prkey_obj)); - if (certs[i].found == 0 && pubkeys[i].found == 0) + if (ckis[i].cert_found == 0 && ckis[i].pubkey_found == 0) continue; /* i.e. no cert or pubkey */ sc_pkcs15_format_id(prkeys[i].id, &prkey_info.id); prkey_info.usage = prkeys[i].usage; prkey_info.native = 1; prkey_info.key_reference = prkeys[i].ref; - prkey_info.modulus_length= prkeys[i].modulus_len; + prkey_info.modulus_length= ckis[i].modulus_len; /* The cert or pubkey should have filled modulus_len */ - /* TODO DSA keys */ + /* TODO EC keys */ sc_format_path(prkeys[i].path, &prkey_info.path); strncpy(prkey_obj.label, prkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1); diff --git a/src/tools/piv-tool.c b/src/tools/piv-tool.c index f4f3d80b..30ff62be 100644 --- a/src/tools/piv-tool.c +++ b/src/tools/piv-tool.c @@ -236,7 +236,7 @@ static int admin_mode(const char* admin_info) return -1; } - r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, &opts); + r = sc_card_ctl(card, SC_CARDCTL_PIV_AUTHENTICATE, &opts); if (r) fprintf(stderr, " admin_mode failed %d\n", r); return r; @@ -248,8 +248,8 @@ static int gen_key(const char * key_info) int r; u8 buf[2]; size_t buflen = 2; - struct sc_cardctl_cryptoflex_genkey_info - keydata = { 0x9a, 1024, 0, NULL, 0}; + sc_cardctl_piv_genkey_info_t + keydata = {0, 0, 0, 0, NULL, 0, NULL, 0, NULL, 0}; unsigned long expl; u8 expc[4]; @@ -271,38 +271,51 @@ static int gen_key(const char * key_info) } switch (buf[1]) { - case 5: keydata.key_bits = 3072; break; - case 6: keydata.key_bits = 1024; break; - case 7: keydata.key_bits = 2048; break; + case 0x05: keydata.key_bits = 3072; break; + case 0x06: keydata.key_bits = 1024; break; + case 0x07: keydata.key_bits = 2048; break; default: - fprintf(stderr, ": algid, 05, 06, 07 for 3072, 1024, 2048\n"); + fprintf(stderr, ": algid:05, 06, 07 for 3072, 1024, 2048\n"); return 2; } - r = sc_card_ctl(card, SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY, &keydata); + keydata.key_algid = buf[1]; + + + r = sc_card_ctl(card, SC_CARDCTL_PIV_GENERATE_KEY, &keydata); if (r) { fprintf(stderr, "gen_key failed %d\n", r); return r; } - - newkey = RSA_new(); - if (newkey == NULL) { - fprintf(stderr, "gen_key RSA_new failed %d\n",r); - return -1; - } - newkey->n = BN_bin2bn(keydata.pubkey, keydata.pubkey_len, newkey->n); - expl = keydata.exponent; - expc[3] = (u8) expl & 0xff; - expc[2] = (u8) (expl >>8) & 0xff; - expc[1] = (u8) (expl >>16) & 0xff; - expc[0] = (u8) (expl >>24) & 0xff; - newkey->e = BN_bin2bn(expc, 4, newkey->e); - - if (verbose) - RSA_print_fp(stdout, newkey,0); - if (bp) - PEM_write_bio_RSAPublicKey(bp, newkey); + if (keydata.key_bits > 0) { /* RSA key */ + RSA * newkey = NULL; + + newkey = RSA_new(); + if (newkey == NULL) { + fprintf(stderr, "gen_key RSA_new failed %d\n",r); + return -1; + } + newkey->n = BN_bin2bn(keydata.pubkey, keydata.pubkey_len, newkey->n); + expl = keydata.exponent; + expc[3] = (u8) expl & 0xff; + expc[2] = (u8) (expl >>8) & 0xff; + expc[1] = (u8) (expl >>16) & 0xff; + expc[0] = (u8) (expl >>24) & 0xff; + newkey->e = BN_bin2bn(expc, 4, newkey->e); + + if (verbose) + RSA_print_fp(stdout, newkey,0); + + if (bp) + PEM_write_bio_RSAPublicKey(bp, newkey); + + } else { + fprintf(stderr, "Unsuported key type\n"); + r = SC_ERROR_UNKNOWN; + } + + return r; @@ -505,8 +518,6 @@ int main(int argc, char * const argv[]) return 1; } - ctx->debug = verbose; - if (action_count <= 0) goto end;