diff --git a/src/libopensc/card-piv.c b/src/libopensc/card-piv.c index 7b2ebb6b..2503dfd0 100644 --- a/src/libopensc/card-piv.c +++ b/src/libopensc/card-piv.c @@ -1,5 +1,5 @@ /* - * card-piv.c: Support for PIV-II from NIST SP800-73 + * card-piv.c: Support for PIV-II from NIST SP800-73 * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 2002 Juha Yrjölä @@ -35,7 +35,7 @@ #include #include #include -#endif /* ENABLE_OPENSSL */ +#endif /* ENABLE_OPENSSL */ #include "internal.h" #include "asn1.h" @@ -112,10 +112,10 @@ enum { * 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 + * 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 + * its certs will be read into the cache and PIV_OBJ_CACHE_VALID set * and PIV_OBJ_CACHE_NOT_PRESENT unset. */ @@ -136,8 +136,10 @@ typedef struct piv_private_data { int selected_obj; /* The index into the piv_objects last selected */ int return_only_cert; /* return the cert from the object */ int rwb_state; /* first time -1, 0, in middle, 1 at eof */ + int operation; /* saved from set_security_env */ + int algorithm; /* saved from set_security_env */ int key_ref; /* saved from set_security_env and */ - int alg_id; /* used in decrypt, signature */ + int alg_id; /* used in decrypt, signature, derive */ int key_size; /* RSA: modulus_bits EC: field_length in bits */ u8* w_buf; /* write_binary buffer */ size_t w_buf_len; /* length of w_buff */ @@ -145,7 +147,7 @@ typedef struct piv_private_data { int keysWithOnCardCerts; int keysWithOffCardCerts; char * offCardCertURL; - int pin_preference; /* set from Discovery object */ + int pin_preference; /* set from Discovery object */ } piv_private_data_t; #define PIV_DATA(card) ((piv_private_data_t*)card->drv_data) @@ -156,21 +158,21 @@ struct piv_aid { size_t len_long; /* With version and other stuff */ u8 *value; }; - + /* * The Generic entry should be the "A0 00 00 03 08 00 00 01 00 " - * NIST published this on 10/6/2005 + * NIST published this on 10/6/2005 * 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 don't need the version number. but could get it from the PIX. + * i.e. "A0 00 00 03 08 00 00 01 00 02 00". + * but we don't 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. - */ + * 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, + {SC_CARD_TYPE_PIV_II_GENERIC, 9, 9, (u8 *) "\xA0\x00\x00\x03\x08\x00\x00\x10\x00" }, {0, 9, 0, NULL } }; @@ -202,16 +204,16 @@ struct piv_object { }; /* Must be in order, and one per enumerated PIV_OBJ */ -static const struct piv_object piv_objects[] = { - { PIV_OBJ_CCC, "Card Capability Container", +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", + { PIV_OBJ_CHUI, "Card Holder Unique Identifier", "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 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} , + { 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} , { 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", + { 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, "Cardholder Facial Images", "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 0}, @@ -220,17 +222,17 @@ static const struct piv_object piv_objects[] = { { PIV_OBJ_X509_KM, "X.509 Certificate for Key Management", "2.16.840.1.101.3.7.2.1.2", 3, "\x5F\xC1\x0B", "\x01\x02", PIV_OBJECT_TYPE_CERT}, { PIV_OBJ_X509_CARD_AUTH, "X.509 Certificate for Card Authentication", - "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}, + "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", + { 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", + "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", @@ -294,11 +296,11 @@ static const struct piv_object piv_objects[] = { "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", + { PIV_OBJ_9B03, "3DES-ECB ADM", "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 0}, /* Only used when signing a cert req, usually from engine - * after piv-tool generated the key and saved the pub key - * to a file. Note RSA key can be 1024, 2048 or 3072 + * after piv-tool generated the key and saved the pub key + * to a file. Note RSA key can be 1024, 2048 or 3072 * but still use the "9x06" name. */ { PIV_OBJ_9A06, "RSA 9A Pub key from last genkey", @@ -352,7 +354,7 @@ static const struct piv_object piv_objects[] = { "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} }; - + static struct sc_card_operations piv_ops; static struct sc_card_driver piv_drv = { @@ -370,7 +372,7 @@ static int piv_find_obj_by_containerid(sc_card_t *card, const u8 * str) 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] + 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); } @@ -386,15 +388,15 @@ static size_t put_tag_and_len(unsigned int tag, size_t len, u8 **ptr) { int i; u8 *p; - - if (len < 128) { + + if (len < 128) { i = 2; - } else if (len < 256) { + } else if (len < 256) { i = 3; - } else { + } else { i = 4; } - + if (ptr) { p = *ptr; *p++ = (u8)tag; @@ -420,23 +422,23 @@ 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 + * 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 from 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 - * caller. that need to be freed by the caller. + * caller. that need to be freed by the caller. */ -static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, +static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, size_t * recvbuflen) { int r; sc_apdu_t apdu; - u8 rbufinitbuf[4096]; + u8 rbufinitbuf[4096]; u8 *rbuf; size_t rbuflen; unsigned int cla_out, tag_out; @@ -445,7 +447,7 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%02x %02x %02x %d : %d %d\n", ins, p1, p2, sendbuflen , card->max_send_size, card->max_recv_size); @@ -461,9 +463,9 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, r = sc_lock(card); if (r != SC_SUCCESS) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); - - sc_format_apdu(card, &apdu, - recvbuf ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT, + + sc_format_apdu(card, &apdu, + recvbuf ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT, ins, p1, p2); apdu.flags |= SC_APDU_FLAGS_CHAINING; @@ -481,19 +483,19 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, apdu.resplen = 0; } - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p", + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p", apdu.flags, apdu.le, apdu.resplen, apdu.resp); /* with new adpu.c and chaining, this actually reads the whole object */ r = sc_transmit_apdu(card, &apdu); - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE r=%d apdu.resplen=%d sw1=%02x sw2=%02x", + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"DEE r=%d apdu.resplen=%d sw1=%02x sw2=%02x", r, apdu.resplen, apdu.sw1, apdu.sw2); if (r < 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed"); goto err; } - + r = sc_check_sw(card, apdu.sw1, apdu.sw2); /* TODO: - DEE look later at tag vs size read too */ @@ -503,15 +505,15 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, } /* - * See how much we read and make sure it is asn1 + * See how much we read and make sure it is asn1 * if not, return 0 indicating no data found - */ + */ + - rbuflen = 0; /* in case rseplen < 3 i.e. not parseable */ if ( recvbuflen && recvbuf && apdu.resplen > 3) { *recvbuflen = 0; - /* we should have all the tag data, so we have to tell sc_asn1_find_tag + /* we should have all the tag data, so we have to tell sc_asn1_find_tag * the buffer is bigger, so it will not produce "ASN1.tag too long!" */ body = rbuf; @@ -519,18 +521,18 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, /* only early beta cards had this problem */ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "***** received buffer tag MISSING "); body = rbuf; - /* some readers/cards might return 6c 00 */ - if (apdu.sw1 == 0x61 || apdu.sw2 == 0x6c ) + /* some readers/cards might return 6c 00 */ + if (apdu.sw1 == 0x61 || apdu.sw2 == 0x6c ) bodylen = 12000; else bodylen = apdu.resplen; } - + rbuflen = body - rbuf + bodylen; /* if using internal buffer, alloc new one */ if (rbuf == rbufinitbuf) { - *recvbuf = malloc(rbuflen); + *recvbuf = malloc(rbuflen); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "DEE got buffer %p len %d",*recvbuf, rbuflen); if (*recvbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; @@ -539,9 +541,9 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2, memcpy(*recvbuf, rbuf, rbuflen); /* copy tag too */ } - } + } - if (recvbuflen) { + if (recvbuflen) { *recvbuflen = rbuflen; r = *recvbuflen; } @@ -555,21 +557,21 @@ err: /* Should use our own keydata, actually should be common to all cards */ /* RSA and EC are added. */ -static int piv_generate_key(sc_card_t *card, +static int piv_generate_key(sc_card_t *card, sc_cardctl_piv_genkey_info_t *keydata) { int r; - u8 *rbuf = NULL; + u8 *rbuf = NULL; size_t rbuflen = 0; u8 *p; const u8 *tag; - u8 tagbuf[16]; + u8 tagbuf[16]; u8 outdata[3]; /* we could also add tag 81 for exponent */ size_t taglen, i; size_t out_len; size_t in_len; unsigned int cla_out, tag_out; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); keydata->exponent = 0; @@ -579,7 +581,7 @@ static int piv_generate_key(sc_card_t *card, keydata->ecparam_len = 0; keydata->ecpoint = NULL; keydata->ecpoint_len = 0; - + out_len = 3; outdata[0] = 0x80; outdata[1] = 0x01; @@ -608,9 +610,9 @@ static int piv_generate_key(sc_card_t *card, memcpy(p, outdata, out_len); p+=out_len; - r = piv_general_io(card, 0x47, 0x00, keydata->key_num, + r = piv_general_io(card, 0x47, 0x00, keydata->key_num, tagbuf, p - tagbuf, &rbuf, &rbuflen); - + if (r >= 0) { const u8 *cp; keydata->exponent = 0; @@ -626,7 +628,7 @@ static int piv_generate_key(sc_card_t *card, sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Tag buffer not found"); goto err; } - + /* if RSA vs EC */ if (keydata->key_bits > 0 ) { tag = sc_asn1_find_tag(card->ctx, cp, in_len, 0x82, &taglen); @@ -637,7 +639,7 @@ static int piv_generate_key(sc_card_t *card, } } 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) @@ -660,7 +662,7 @@ static int piv_generate_key(sc_card_t *card, * and sign req in single operation */ r = 0; } - + err: if (rbuf) free(rbuf); @@ -678,7 +680,7 @@ static int piv_select_aid(sc_card_t* card, u8* aid, size_t aidlen, u8* response, "Got args: aid=%x, aidlen=%d, response=%x, responselen=%d\n", aid, aidlen, response, responselen ? *responselen : 0); - sc_format_apdu(card, &apdu, + sc_format_apdu(card, &apdu, response == NULL ? SC_APDU_CASE_3_SHORT : SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); apdu.lc = aidlen; apdu.data = aid; @@ -713,10 +715,10 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* first see if the default applcation will return a template - * that we know about. + * that we know about. */ - if (card->type == SC_CARD_TYPE_PIV_II_GENERIC) + if (card->type == SC_CARD_TYPE_PIV_II_GENERIC) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, rbuf, &resplen); @@ -724,12 +726,12 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) tag = sc_asn1_find_tag(card->ctx, rbuf, resplen, 0x61, &taglen); if (tag != NULL) { pix = sc_asn1_find_tag(card->ctx, tag, taglen, 0x4F, &pixlen); - if (pix != NULL ) { + if (pix != NULL ) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"found PIX"); - + /* early cards returned full AID, rather then just the pix */ for (i = 0; piv_aids[i].len_long != 0; i++) { - if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, + if ((pixlen >= 6 && memcmp(pix, piv_aids[i].value + 5, piv_aids[i].len_long - 5 ) == 0) || ((pixlen >= piv_aids[i].len_short && memcmp(pix, piv_aids[i].value, @@ -747,19 +749,19 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) } } - /* for testing, we can force the use of a specific AID - * by using the card= parameter in conf file + /* for testing, we can force the use of a specific AID + * by using the card= parameter in conf file */ for (i = 0; piv_aids[i].len_long != 0; i++) { if (card->type > SC_CARD_TYPE_PIV_II_BASE && card->type < SC_CARD_TYPE_PIV_II_BASE+1000 && card->type != piv_aids[i].enumtag) { continue; - } + } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0x04, 0x00); apdu.lc = piv_aids[i].len_long; apdu.data = piv_aids[i].value; - + apdu.datalen = apdu.lc; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); @@ -769,38 +771,38 @@ static int piv_find_aid(sc_card_t * card, sc_file_t *aid_file) SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); - + if (r) { if (card->type != 0 && card->type == piv_aids[i].enumtag) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } - continue; + continue; } - if ( apdu.resplen == 0 && r == 0) { + if ( apdu.resplen == 0 && r == 0) { /* could be the MSU card */ continue; /* other cards will return a FCI */ } - if (apdu.resp[0] != 0x6f || apdu.resp[1] > apdu.resplen - 2 ) + if (apdu.resp[0] != 0x6f || apdu.resp[1] > apdu.resplen - 2 ) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); - + card->ops->process_fci(card, aid_file, apdu.resp+2, apdu.resp[1]); - if (aid_file->name == NULL) + if (aid_file->name == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NO_CARD_SUPPORT); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, i); } - + 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. +/* + * 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. + * 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) @@ -813,7 +815,7 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, const u8 * body; unsigned int cla_out, tag_out; size_t bodylen; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); *buf = NULL; @@ -822,7 +824,7 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, 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; + r = SC_ERROR_FILE_NOT_FOUND; goto err; } len = read(f, tagbuf, sizeof(tagbuf)); /* get tag and length */ @@ -832,8 +834,8 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, goto err; } body = tagbuf; - if (sc_asn1_read_tag(&body, 0xfffff, &cla_out, - &tag_out, &bodylen) != SC_SUCCESS) { + 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; @@ -846,7 +848,7 @@ static int piv_read_obj_from_file(sc_card_t * card, char * filename, } memcpy(*buf, tagbuf, len); /* copy first or only part */ if (rbuflen > len) { - len = read(f, *buf + sizeof(tagbuf), rbuflen - sizeof(tagbuf)); /* read rest */ + len = read(f, *buf + sizeof(tagbuf), rbuflen - sizeof(tagbuf)); /* read rest */ if (len != rbuflen - sizeof(tagbuf)) { r = SC_ERROR_INVALID_ASN1_OBJECT; free (*buf); @@ -863,19 +865,19 @@ err: } /* the tag is the PIV_OBJ_* */ -static int piv_get_data(sc_card_t * card, int enumtag, +static int piv_get_data(sc_card_t * card, int enumtag, u8 **buf, size_t *buf_len) { u8 *p; int r = 0; u8 tagbuf[8]; size_t tag_len; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "#%d \n", enumtag); /* assert(enumtag >= 0 && enumtag < PIV_OBJ_LAST_ENUM); */ - + tag_len = piv_objects[enumtag].tag_len; p = tagbuf; @@ -904,10 +906,10 @@ static int piv_get_data(sc_card_t * card, int enumtag, goto err; } *buf_len = r; - } else if ( r == 0) { + } else if ( r == 0) { r = SC_ERROR_FILE_NOT_FOUND; goto err; - } else { + } else { goto err; } } @@ -920,7 +922,7 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"get buffer for #%d len %d", enumtag, *b } } - r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, + r = piv_general_io(card, 0xCB, 0x3F, 0xFF, tagbuf, p - tagbuf, buf, buf_len); err: @@ -936,7 +938,7 @@ static int piv_get_cached_data(sc_card_t * card, int enumtag, int r; u8 *rbuf = NULL; size_t rbuflen; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "#%d", enumtag); @@ -951,8 +953,8 @@ static int piv_get_cached_data(sc_card_t * card, int enumtag, priv->obj_cache[enumtag].obj_len, priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); - - + + if (priv->obj_cache[enumtag].obj_len == 0) { r = SC_ERROR_FILE_NOT_FOUND; sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"#%d found but len=0", @@ -965,21 +967,21 @@ static int piv_get_cached_data(sc_card_t * card, int enumtag, goto ok; } - /* + /* * 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 + * 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 */ + /* 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; + rbuflen = 1; r = piv_get_data(card, enumtag, &rbuf, &rbuflen); if (r > 0) { priv->obj_cache[enumtag].flags |= PIV_OBJ_CACHE_VALID; @@ -997,7 +999,7 @@ static int piv_get_cached_data(sc_card_t * card, int 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; @@ -1020,42 +1022,42 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) /* if already cached */ if (priv->obj_cache[enumtag].internal_obj_data && priv->obj_cache[enumtag].internal_obj_len) { - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"#%d found internal %p:%d", enumtag, - priv->obj_cache[enumtag].internal_obj_data, + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"#%d found internal %p:%d", enumtag, + priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); - } + } - body = sc_asn1_find_tag(card->ctx, - priv->obj_cache[enumtag].obj_data, + body = sc_asn1_find_tag(card->ctx, + priv->obj_cache[enumtag].obj_data, priv->obj_cache[enumtag].obj_len, 0x53, &bodylen); - if (body == NULL) + if (body == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); - + /* get the certificate out */ - if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CERT) { - + if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_CERT) { + 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 = sc_asn1_find_tag(card->ctx, body, bodylen, 0x70, &taglen); - if (tag == NULL) + if (tag == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); - + if (taglen == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); - + if(compressed) { #ifdef ENABLE_ZLIB size_t len; u8* newBuf = NULL; if(SC_SUCCESS != sc_decompress_alloc(&newBuf, &len, tag, taglen, COMPRESSION_AUTO)) { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OBJECT_NOT_VALID); - } + } priv->obj_cache[enumtag].internal_obj_data = newBuf; priv->obj_cache[enumtag].internal_obj_len = len; #else @@ -1071,7 +1073,7 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) } /* convert pub key to internal */ -/* TODO: -DEE need to fix ... would only be used if we cache the pub key, but we don't today */ +/* TODO: -DEE need to fix ... would only be used if we cache the pub key, but we don't today */ } else if (piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, *body, &taglen); @@ -1089,19 +1091,19 @@ static int piv_cache_internal_data(sc_card_t *card, int enumtag) } else { SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); } - - sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"added #%d internal %p:%d", enumtag, + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"added #%d internal %p:%d", enumtag, priv->obj_cache[enumtag].internal_obj_data, priv->obj_cache[enumtag].internal_obj_len); - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); } -/* +/* * Callers of this may be expecting a certificate, - * select file will have saved the object type for us - * as well as set that we want the cert from the object. + * select file will have saved the object type for us + * as well as set that we want the cert from the object. */ static int piv_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) @@ -1115,13 +1117,13 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, size_t bodylen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - if (priv->selected_obj < 0) + if (priv->selected_obj < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); enumtag = piv_objects[priv->selected_obj].enumtag; if (priv->rwb_state == -1) { r = piv_get_cached_data(card, enumtag, &rbuf, &rbuflen); - + if (r >=0) { /* an object with no data will be considered not found */ /* Discovery tag = 0x73, all others are 0x53 */ @@ -1147,10 +1149,10 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, /* if cached obj has internal interesting data (cert or pub key) */ if (priv->return_only_cert || piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) { r = piv_cache_internal_data(card, enumtag); - if (r < 0) + if (r < 0) goto err; } - + } priv->rwb_state = 0; } @@ -1165,11 +1167,11 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx, /* rbuf rbuflen has pointer and length to cached data */ if ( rbuflen < idx + count) - count = rbuflen - idx; - if (count <= 0) { + count = rbuflen - idx; + if (count <= 0) { r = 0; priv->rwb_state = 1; - } else { + } else { memcpy(buf, rbuf + idx, count); r = count; } @@ -1177,14 +1179,14 @@ err: SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } - + /* - * the tag is the PIV_OBJ_* - * The buf should have the 0x53 tag+len+tags and data + * the tag is the PIV_OBJ_* + * The buf should have the 0x53 tag+len+tags and data */ -static int piv_put_data(sc_card_t *card, int tag, - const u8 *buf, size_t buf_len) +static int piv_put_data(sc_card_t *card, int tag, + const u8 *buf, size_t buf_len) { int r; u8 * sbuf; @@ -1196,18 +1198,18 @@ static int piv_put_data(sc_card_t *card, int tag, tag_len = piv_objects[tag].tag_len; sbuflen = put_tag_and_len(0x5c, tag_len, NULL) + buf_len; - if (!(sbuf = malloc(sbuflen))) + if (!(sbuf = malloc(sbuflen))) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); p = sbuf; put_tag_and_len(0x5c, tag_len, &p); memcpy(p, piv_objects[tag].tag_value, tag_len); p += tag_len; - + memcpy(p, buf, buf_len); p += buf_len; - r = piv_general_io(card, 0xDB, 0x3F, 0xFF, + r = piv_general_io(card, 0xDB, 0x3F, 0xFF, sbuf, p - sbuf, NULL, NULL); if (sbuf) @@ -1235,7 +1237,7 @@ static int piv_write_certificate(sc_card_t *card, sbuflen = put_tag_and_len(0x53, taglen, NULL); sbuf = malloc(sbuflen); - if (sbuf == NULL) + if (sbuf == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); p = sbuf; put_tag_and_len(0x53, taglen, &p); @@ -1257,24 +1259,24 @@ static int piv_write_certificate(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } -/* - * For certs we need to add the 0x53 tag and other specific tags, - * and call the piv_put_data - * Note: the select file will have saved the object type for us +/* + * For certs we need to add the 0x53 tag and other specific tags, + * and call the piv_put_data + * Note: the select file will have saved the object type for us * Write is used by piv-tool, so we will use flags: * length << 8 | 8bits: * object xxxx0000 * uncompresed cert xxx00001 * compressed cert xxx10001 * pubkey xxxx0010 - * + * * to indicate we are writing a cert and if is compressed - * or if we are writing a pubkey in to the cache. - * if its not a cert or pubkey its an object. + * or if we are writing a pubkey in to the cache. + * if its not a cert or pubkey its an object. * * Therefore when idx=0, we will get the length of the object * and allocate a buffer, so we can support partial writes. - * When the last chuck of the data is sent, we will write it. + * When the last chuck of the data is sent, we will write it. */ static int piv_write_binary(sc_card_t *card, unsigned int idx, @@ -1314,7 +1316,7 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, if (idx != 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NO_CARD_SUPPORT); - priv->w_buf_len = flags>>8; + priv->w_buf_len = flags>>8; if (priv->w_buf_len == 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL); @@ -1336,13 +1338,13 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, count); priv-> rwb_state = 1; /* at end of object */ - + switch (flags & 0x0f) { case 1: r = piv_write_certificate(card, priv->w_buf, priv->w_buf_len, flags & 0x10); break; - case 2: /* pubkey to be added to cache, it should have 0x53 and 0x99 tags. */ + case 2: /* pubkey to be added to cache, it should have 0x53 and 0x99 tags. */ /* TODO: -DEE this is not fully implemented and not used */ r = priv->w_buf_len; break; @@ -1361,22 +1363,22 @@ static int piv_write_binary(sc_card_t *card, unsigned int idx, } priv->w_buf = NULL; priv->w_buf_len = 0; - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, (r < 0)? r : (int)count); + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, (r < 0)? r : (int)count); } /* - * Card initialization is not standard. - * Some cards use mutual or external authentication using s 3des key. We - * will read in the key from a file. - * This is only needed during initialization/personalization of the card + * Card initialization is not standard. + * Some cards use mutual or external authentication using s 3des key. We + * will read in the key from a file. + * This is only needed during initialization/personalization of the card */ static int piv_get_3des_key(sc_card_t *card, u8 *key) { int r; - int f = -1; - char keybuf[24*3]; /* 3des key as three sets of xx:xx:xx:xx:xx:xx:xx:xx + int f = -1; + char keybuf[24*3]; /* 3des key as three sets of xx:xx:xx:xx:xx:xx:xx:xx * with a : between which is 71 bytes */ char * keyfilename = NULL; size_t outlen; @@ -1398,7 +1400,7 @@ static int piv_get_3des_key(sc_card_t *card, u8 *key) } if (read(f, keybuf, 71) != 71) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," Unable to read 3des key for general_external_authenticate\n"); - r = SC_ERROR_WRONG_LENGTH; + r = SC_ERROR_WRONG_LENGTH; goto err; } keybuf[23] = '\0'; @@ -1413,11 +1415,11 @@ static int piv_get_3des_key(sc_card_t *card, u8 *key) outlen = 8; r = sc_hex_to_bin(keybuf+48, key+16, &outlen); if (r) goto err; - + err: if (f >=0) close(f); - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } @@ -1428,7 +1430,7 @@ err: * challenges, nonces etc from card are less then 114 (keeps tags simple) */ -static int piv_general_mutual_authenticate(sc_card_t *card, +static int piv_general_mutual_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; @@ -1472,7 +1474,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, /* get the encrypted nonce */ - r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); + r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); if (r < 0) goto err; q = rbuf; @@ -1485,13 +1487,13 @@ static int piv_general_mutual_authenticate(sc_card_t *card, } N = *(rbuf + 3); /* assuming N + sizeof(nonce) + 6 < 128 */ - /* prepare the response */ + /* prepare the response */ p = sbuf; *p++ = 0x7c; *p++ = N + sizeof(nonce)+ 4; *p++ = 0x80; - *p++ = (u8)N; - + *p++ = (u8)N; + /* decrypt the data from the card */ if (!EVP_DecryptInit(&ctx, cipher, key, NULL)) { /* may fail if des parity of key is wrong. depends on OpenSSL options */ @@ -1512,7 +1514,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = SC_ERROR_INTERNAL; goto err; } - + p += N; *p++ = 0x81; @@ -1523,7 +1525,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, free(rbuf); rbuf = NULL; - r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); + r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, &rbuf, &rbuflen); if (r < 0) goto err; q = rbuf; if ( (*q++ != 0x7C) @@ -1533,8 +1535,8 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = SC_ERROR_INVALID_DATA; goto err; } - N = *(rbuf + 3); - + N = *(rbuf + 3); + p = sbuf; EVP_CIPHER_CTX_cleanup(&ctx); @@ -1553,7 +1555,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, r = SC_ERROR_INTERNAL; goto err; } - + if (outl+outl2 != sizeof(nonce) || memcmp(nonce, p, sizeof(nonce)) != 0) { sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "mutual authentication failed, card returned wrong value"); r = SC_ERROR_DECRYPT_FAILED; @@ -1563,7 +1565,7 @@ static int piv_general_mutual_authenticate(sc_card_t *card, err: EVP_CIPHER_CTX_cleanup(&ctx); - if (locked) + if (locked) sc_unlock(card); if (rbuf) free(rbuf); @@ -1577,7 +1579,7 @@ err: } /* Currently only used for card administration */ -static int piv_general_external_authenticate(sc_card_t *card, +static int piv_general_external_authenticate(sc_card_t *card, unsigned int key_ref, unsigned int alg_id) { int r; @@ -1591,7 +1593,7 @@ static int piv_general_external_authenticate(sc_card_t *card, u8 *p, *q; EVP_CIPHER_CTX ctx; const EVP_CIPHER *cipher; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); EVP_CIPHER_CTX_init(&ctx); @@ -1603,7 +1605,7 @@ static int piv_general_external_authenticate(sc_card_t *card, case 4: cipher=EVP_des_ede3_cbc(); break; default: cipher=EVP_des_ede3_ecb(); break; } - + r = piv_get_3des_key(card, key); if (r != SC_SUCCESS) goto err; @@ -1648,7 +1650,7 @@ static int piv_general_external_authenticate(sc_card_t *card, if (!EVP_EncryptUpdate(&ctx, p, &outl, q, N)) { r = SC_ERROR_INTERNAL; goto err; - } + } if(!EVP_EncryptFinal(&ctx, p+outl, &outl2)) { r = SC_ERROR_INTERNAL; goto err; @@ -1658,7 +1660,7 @@ static int piv_general_external_authenticate(sc_card_t *card, goto err; } p += N; - + r = piv_general_io(card, 0x87, alg_id, key_ref, sbuf, p - sbuf, NULL, NULL); err: @@ -1700,9 +1702,9 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri /* Note: We need the temp because (some?) Oberthur cards don't like selecting an applet without response data */ /* 800-73-3 part1 draft, and CIO Council docs imply for PIV Compatible card * The FASC-N Agency code should be 9999 and there should be a GUID - * based on RFC 4122. RIf so and the GUID is not all 0's + * based on RFC 4122. RIf so and the GUID is not all 0's * we will use the GUID as the serial number. - */ + */ piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen); r = piv_get_cached_data(card, PIV_OBJ_CHUI, &rbuf, &rbuflen); @@ -1720,20 +1722,20 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri for (i = 0; i < 16; i++) { gbits = gbits | guid[i]; /* if all are zero, gbits will be zero */ } - } + } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"fascn=%p,fascnlen=%d,guid=%p,guidlen=%d,gbits=%2.2x\n", fascn, fascnlen, guid, guidlen, gbits); if (fascn && fascnlen == 25) { /* test if guid and the fascn starts with ;9999 (in ISO 4bit + partiy code) */ - if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7 + if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7 && fascn[2] == 0x39 && (fascn[3] | 0x7F) == 0xFF)) { serial->len = fascnlen < SC_MAX_SERIALNR ? fascnlen : SC_MAX_SERIALNR; memcpy (serial->value, fascn, serial->len); r = SC_SUCCESS; gbits = 0; /* set to skip using guid below */ } - } + } if (guid && gbits) { serial->len = guidlen < SC_MAX_SERIALNR ? guidlen : SC_MAX_SERIALNR; memcpy (serial->value, guid, serial->len); @@ -1742,19 +1744,19 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri } } - card->serialnr = *serial; + card->serialnr = *serial; 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. + * 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. + * 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. + * pkcs15-piv.c calls this via cardctl. */ static int piv_is_object_present(sc_card_t *card, u8 *ptr) @@ -1762,8 +1764,8 @@ 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); + + enumtag = piv_find_obj_by_containerid(card, ptr); if (enumtag >= 0 && priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_NOT_PRESENT) r = 1; @@ -1773,7 +1775,7 @@ static int piv_is_object_present(sc_card_t *card, u8 *ptr) /* * 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. + * called by pkcs15-piv.c via cardctl when setting up the pins. */ static int piv_get_pin_preference(sc_card_t *card, int *ptr) { @@ -1787,7 +1789,7 @@ 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 */ - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"cmd=%ld ptr=%p"); if (priv == NULL) { @@ -1798,17 +1800,17 @@ static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) opts = (u8 *)ptr; switch (*opts) { case 'A': - return piv_general_external_authenticate(card, + return piv_general_external_authenticate(card, *(opts+1), *(opts+2)); break; case'M': - return piv_general_mutual_authenticate(card, + return piv_general_mutual_authenticate(card, *(opts+1), *(opts+2)); break; } break; case SC_CARDCTL_PIV_GENERATE_KEY: - return piv_generate_key(card, + return piv_generate_key(card, (sc_cardctl_piv_genkey_info_t *) ptr); break; case SC_CARDCTL_GET_SERIALNR: @@ -1834,10 +1836,10 @@ static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"challenge len=%d",len); - sc_lock(card); + sc_lock(card); p = sbuf; *p++ = 0x7c; @@ -1845,15 +1847,15 @@ static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) *p++ = 0x81; *p++ = 0x00; - /* assuming 8 byte response ? */ + /* assuming 8 byte response ? */ /* should take what the card returns */ while (len > 0) { size_t n = len > 8 ? 8 : len; /* NIST 800-73-3 says use 9B, previous verisons used 00 */ - r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, p - sbuf, - &rbuf, &rbuflen); - if (r < 0) { + r = piv_general_io(card, 0x87, 0x00, 0x9B, sbuf, p - sbuf, + &rbuf, &rbuflen); + if (r < 0) { sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } @@ -1873,7 +1875,7 @@ static int piv_get_challenge(sc_card_t *card, u8 *rnd, size_t len) rbuf = NULL; } - sc_unlock(card); + sc_unlock(card); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 0); @@ -1885,14 +1887,17 @@ static int piv_set_security_env(sc_card_t *card, { piv_private_data_t * priv = PIV_DATA(card); int r = 0; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"flags=%08x op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%d\n", - env->flags, env->operation, env->algorithm, env->algorithm_flags, + env->flags, env->operation, env->algorithm, env->algorithm_flags, env->algorithm_ref, env->key_ref[0], env->key_ref_len); - if (env->algorithm == SC_ALGORITHM_RSA) { + priv->operation = env->operation; + priv->algorithm = env->algorithm; + + if (env->algorithm == SC_ALGORITHM_RSA) { priv->alg_id = 0x06; /* Say it is RSA, set 5, 6, 7 later */ } else if (env->algorithm == SC_ALGORITHM_EC) { if (env->flags & SC_SEC_ENV_ALG_REF_PRESENT) { @@ -1926,7 +1931,7 @@ static int piv_restore_security_env(sc_card_t *card, int se_num) } -static int piv_validate_general_authentication(sc_card_t *card, +static int piv_validate_general_authentication(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { @@ -1942,23 +1947,27 @@ static int piv_validate_general_authentication(sc_card_t *card, u8 sbuf[4096]; /* needs work. for 3072 keys, needs 384+10 or so */ u8 *rbuf = NULL; size_t rbuflen; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* should assume large send data */ p = sbuf; put_tag_and_len(0x7c, (2 + put_tag_and_len(0, datalen, NULL)) , &p); put_tag_and_len(0x82, 0, &p); - put_tag_and_len(0x81, datalen, &p); + if (priv->operation == SC_SEC_OPERATION_DERIVE + && priv->algorithm == SC_ALGORITHM_EC) + put_tag_and_len(0x85, datalen, &p); + else + put_tag_and_len(0x81, datalen, &p); memcpy(p, data, datalen); p += datalen; - /* - * alg_id=06 is a place holder for all RSA keys. + /* + * alg_id=06 is a place holder for all RSA keys. * Derive the real alg_id based on the size of the - * the data, as we are always using raw mode. - * Non RSA keys needs some work in thia area. + * the data, as we are always using raw mode. + * Non RSA keys needs some work in thia area. */ real_alg_id = priv->alg_id; @@ -1971,14 +1980,14 @@ static int piv_validate_general_authentication(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_NO_CARD_SUPPORT); } } - /* EC alg_id was already set */ + /* EC alg_id was already set */ - r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref, - sbuf, p - sbuf, &rbuf, &rbuflen); + r = piv_general_io(card, 0x87, real_alg_id, priv->key_ref, + sbuf, p - sbuf, &rbuf, &rbuflen); if ( r >= 0) { body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x7c, &bodylen); - + if (body) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x82, &taglen); if (tag) { @@ -1987,7 +1996,7 @@ static int piv_validate_general_authentication(sc_card_t *card, } } else r = SC_ERROR_INVALID_DATA; - } + } if (rbuf) free(rbuf); @@ -1995,11 +2004,11 @@ static int piv_validate_general_authentication(sc_card_t *card, SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } -static int piv_compute_signature(sc_card_t *card, +static int piv_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { - piv_private_data_t * priv = PIV_DATA(card); + piv_private_data_t * priv = PIV_DATA(card); int r; int i; int nLen; @@ -2012,14 +2021,14 @@ static int piv_compute_signature(sc_card_t *card, SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); - /* The PIV returns a DER SEQUENCE{INTEGER, INTEGER} + /* The PIV returns a DER SEQUENCE{INTEGER, INTEGER} * Which may have leading 00 to force positive * TODO: -DEE should check if PKCS15 want the same * But PKCS11 just wants 2* filed_length in bytes * So we have to strip out the integers - * if present and pad on left if too short. + * if present and pad on left if too short. */ - + if (priv->alg_id == 0x11 || priv->alg_id == 0x14 ) { nLen = (priv->key_size + 7) / 8; if (outlen < 2*nLen) { @@ -2028,21 +2037,21 @@ static int piv_compute_signature(sc_card_t *card, goto err; } memset(out, 0, outlen); - + r = piv_validate_general_authentication(card, data, datalen, rbuf, rbuflen); - if (r < 0) + if (r < 0) goto err; - + if ( r >= 0) { body = sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x30, &bodylen); - + for (i = 0; i<2; i++) { if (body) { tag = sc_asn1_find_tag(card->ctx, body, bodylen, 0x02, &taglen); if (tag) { bodylen -= taglen - (tag - body); body = tag + taglen; - + if (taglen > nLen) { /* drop leading 00 if present */ if (*tag != 0x00) { r = SC_ERROR_INVALID_DATA; @@ -2083,12 +2092,12 @@ static int piv_decipher(sc_card_t *card, /* * the PIV-II does not always support files, but we will simulate * files and reading/writing using get/put_data - * The path is the containerID number + * The path is the containerID number * We can use this to determine the type of data requested, like a cert - * or pub key. - * We only support write from the piv_tool with file_out==NULL - * All other requests should be to read. - * Only if file_out != null, will we read to get length. + * or pub key. + * We only support write from the piv_tool with file_out==NULL + * All other requests should be to read. + * Only if file_out != null, will we read to get length. */ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) @@ -2101,59 +2110,59 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t *file = NULL; u8 * rbuf = NULL; size_t rbuflen = 0; - + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); path = in_path->value; pathlen = in_path->len; - + /* only support single EF in current application */ if (memcmp(path, "\x3F\x00", 2) == 0) { if (pathlen == 2) { r = piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, NULL, NULL); SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Cannot select PIV AID"); - - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } else if (pathlen > 2) { path += 2; pathlen -= 2; } } - + i = piv_find_obj_by_containerid(card, path); if (i < 0) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); - + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); + /* - * pkcs15 will use a 2 byte path or a 4 byte path - * with cece added to path to request only the cert from the cert obj + * pkcs15 will use a 2 byte path or a 4 byte path + * with cece added to path to request only the cert from the cert obj * PIV "Container ID" is used as the path, and are two bytes long */ priv->return_only_cert = (pathlen == 4 && path[2] == 0xce && path[3] == 0xce); - + priv->selected_obj = i; priv->rwb_state = -1; - + /* make it look like the file was found. */ - /* We don't want to read it now unless we need the length */ - + /* We don't want to read it now unless we need the length */ + if (file_out) { /* we need to read it now, to get length into cache */ - r = piv_get_cached_data(card, i, &rbuf, &rbuflen); - + r = piv_get_cached_data(card, i, &rbuf, &rbuflen); + if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND); /* get the cert or the pub key out and into the cache too */ - if (priv->return_only_cert || piv_objects[i].flags & PIV_OBJECT_TYPE_PUBKEY) { + if (priv->return_only_cert || piv_objects[i].flags & PIV_OBJECT_TYPE_PUBKEY) { r = piv_cache_internal_data(card, i); if (r < 0) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); } - + file = sc_file_new(); if (file == NULL) SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); @@ -2165,7 +2174,7 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path, file->ef_structure = 0; if (priv->return_only_cert) file->size = priv->obj_cache[i].internal_obj_len; - else + else file->size = priv->obj_cache[i].obj_len; file->id = (piv_objects[i].containerid[0]<<8) + piv_objects[i].containerid[1]; @@ -2191,25 +2200,25 @@ static int piv_process_discovery(sc_card_t *card) size_t pinplen; unsigned int cla_out, tag_out; - + r = piv_get_cached_data(card, PIV_OBJ_DISCOVERY, &rbuf, &rbuflen); - if (r <= 0) { + 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. + * and pree 800-73-3 cards may treat this as a strange error. * So treat any error as not present */ - r = 0; + 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; + 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; + r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } @@ -2219,7 +2228,7 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery 0x%2.2x 0x%2.2x %p:%d", 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 || + 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 */ @@ -2244,11 +2253,11 @@ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Discovery pinp flags=0x%2.2x 0x%2.2x",* /* * 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. + * ahead of time, and if so will use it for the certs listed. * TODO: -DEE - * 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. - */ + * 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) { @@ -2291,10 +2300,10 @@ static int piv_process_history(sc_card_t *card) /* the object is now cached, see what we have */ if (rbuflen != 0) { - body = rbuf; + 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; + r = SC_ERROR_INVALID_ASN1_OBJECT; goto err; } @@ -2302,7 +2311,7 @@ static int piv_process_history(sc_card_t *card) numlen = 0; num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC1, &numlen); if (num) { - if (numlen != 1 || + 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; @@ -2313,7 +2322,7 @@ static int piv_process_history(sc_card_t *card) numlen = 0; num = sc_asn1_find_tag(card->ctx, body, bodylen, 0xC2, &numlen); if (num) { - if (numlen != 1 || + 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; @@ -2334,7 +2343,7 @@ static int piv_process_history(sc_card_t *card) } } sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "History on=%d off=%d URL=%s\n", - priv->keysWithOnCardCerts, priv->keysWithOffCardCerts, + priv->keysWithOnCardCerts, priv->keysWithOffCardCerts, priv->offCardCertURL ? priv->offCardCertURL:"NONE"); /* now mark what objects are on the card */ @@ -2344,18 +2353,18 @@ static int piv_process_history(sc_card_t *card) /* * 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. - * + * 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 + * 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]; + char filename[PATH_MAX]; if (strncmp("http://", priv->offCardCertURL, 7)) { r = SC_ERROR_INVALID_DATA; @@ -2366,14 +2375,14 @@ static int piv_process_history(sc_card_t *card) 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, + r = sc_get_cache_dir(card->ctx, filename, sizeof(filename) - strlen(fp) - 2); - if (r != SC_SUCCESS) - goto err; + if (r != SC_SUCCESS) + goto err; #ifdef _WIN32 strcat(filename,"\\"); #else @@ -2388,19 +2397,19 @@ static int piv_process_history(sc_card_t *card) goto err; } - /* - * Its a seq of seq of a key ref and cert + /* + * 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 || + &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; + goto err; } - seq = body; + seq = body; while (bodylen > 0) { seqtag = seq; if (sc_asn1_read_tag(&seq, bodylen, &cla_out, @@ -2412,7 +2421,7 @@ static int piv_process_history(sc_card_t *card) } keyref = sc_asn1_find_tag(card->ctx, seq, seqlen, 0x04, &keyreflen); - if (!keyref || keyreflen != 1 || + 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; @@ -2420,11 +2429,11 @@ static int piv_process_history(sc_card_t *card) } cert = keyref + keyreflen; certlen = seqlen - (cert - seq); - - enumtag = PIV_OBJ_RETIRED_X509_1 + *keyref - 0x82; + + enumtag = PIV_OBJ_RETIRED_X509_1 + *keyref - 0x82; /* now add the cert like another object */ - - i2 = put_tag_and_len(0x70,certlen, NULL) + + i2 = put_tag_and_len(0x70,certlen, NULL) + put_tag_and_len(0x71, 1, NULL) + put_tag_and_len(0xFE, 0, NULL); @@ -2436,7 +2445,7 @@ static int piv_process_history(sc_card_t *card) goto err; } cp = certobj; - put_tag_and_len(0x53, i2, &cp); + put_tag_and_len(0x53, i2, &cp); put_tag_and_len(0x70,certlen, &cp); memcpy(cp, cert, certlen); cp += certlen; @@ -2444,7 +2453,7 @@ static int piv_process_history(sc_card_t *card) *cp++ = 0x00; put_tag_and_len(0xFE, 0, &cp); - priv->obj_cache[enumtag].obj_data = certobj; + 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; @@ -2461,7 +2470,7 @@ static int piv_process_history(sc_card_t *card) priv->obj_cache[enumtag].obj_len, *keyref); bodylen -= (seqlen + seq - seqtag); - seq += seqlen; + seq += seqlen; } } err: @@ -2474,7 +2483,7 @@ err: static int piv_finish(sc_card_t *card) { piv_private_data_t * priv = PIV_DATA(card); - int i; + int i; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv) { @@ -2485,7 +2494,7 @@ static int piv_finish(sc_card_t *card) 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, 0x%02x %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); @@ -2530,14 +2539,14 @@ static int piv_init(sc_card_t *card) 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", + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Max send = %d recv = %d\n", card->max_send_size, card->max_recv_size); card->drv_data = priv; card->cla = 0x00; @@ -2551,21 +2560,26 @@ static int piv_init(sc_card_t *card) priv->enumtag = piv_aids[r].enumtag; card->type = piv_aids[r].enumtag; - flags = SC_ALGORITHM_RSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN; - + /* PKCS#11 may try to generate session keys, and get confused + * if SC_ALGORITHM_ONBOARD_KEY_GEN is present + * piv-tool can still do this, just don't tell PKCS#11 + */ + + flags = SC_ALGORITHM_RSA_RAW; + _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */ - - flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ONBOARD_KEY_GEN; - ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; + + flags = SC_ALGORITHM_ECDSA_RAW; + ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; _sc_card_add_ec_alg(card, 256, flags, ext_flags); _sc_card_add_ec_alg(card, 384, flags, ext_flags); - + 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. @@ -2580,7 +2594,7 @@ static int piv_init(sc_card_t *card) } -static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, +static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { /* Extra validation of (new) PIN during a PIN change request, to @@ -2611,7 +2625,7 @@ static struct sc_card_driver * sc_get_driver(void) piv_ops.match_card = piv_match_card; piv_ops.init = piv_init; piv_ops.finish = piv_finish; - + piv_ops.select_file = piv_select_file; /* must use get/put, could emulate? */ piv_ops.get_challenge = piv_get_challenge; piv_ops.read_binary = piv_read_binary;