demo PIV-II patches to OpenSC

classic Classic list List threaded Threaded
1 message Options
Reply | Threaded
Open this post in threaded view
|

demo PIV-II patches to OpenSC

Douglas E. Engert
Attached are patches to OpenSC svn 2478 to add minimal support for the
PIV-II cards as defined NIST SP800-73. The code is designed to
allow PKCS11 via PKCS15 to load a certificate and use the private
key. I have been testing this using a Oberthur Beta PIV applet,
on Linux.

(Note the patch to iso7816.c for the get_response. I believe this is a fix
for a general bug, as get response can return 61XX among other returns,
and the return needs to be passed back.)


Doing the general authenticate, generating a key pair, and using it
in a cert request depends on the card and the keys used for the
general authenticate.

The card-piv.c will look for two environment variables while doing
admin type operations:
  PIV_9B03_KEY   which points at a 3des key in the form of xx:xx:...:xx
                 that is used by the general authenticate.

  PIV_9A06_KEY  points at a file where the public key will be written
                when it is generated, or read if it is needed.  SP800-73 does
                not define an operation to read the public key off the card
                other then when it is generated.

If anyone is interested, I can go into further details on using OpenSSL
with the engine code to generate a certificate request using the above.



--

  Douglas E. Engert  <[hidden email]>
  Argonne National Laboratory
  9700 South Cass Avenue
  Argonne, Illinois  60439
  (630) 252-5444

Index: src/tools/pkcs15-tool.c
===================================================================
--- src/tools/pkcs15-tool.c (revision 2478)
+++ src/tools/pkcs15-tool.c (working copy)
@@ -327,6 +327,8 @@
  r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
  if (r) {
  fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
+ if (r == SC_ERROR_FILE_NOT_FOUND)
+ continue; /* DEE emulation may say there is a file */
  return 1;
  }
  r = print_data_object("Data Object", data_object->data, data_object->data_len);
@@ -371,6 +373,8 @@
  r = sc_pkcs15_read_data_object(p15card, cinfo, &data_object);
  if (r) {
  fprintf(stderr, "Data object read failed: %s\n", sc_strerror(r));
+ if (r == SC_ERROR_FILE_NOT_FOUND)
+ continue; /* DEE emulation may say there is a file */
  return 1;
  }
  r = list_data_object("Data Object", data_object->data, data_object->data_len);
Index: src/tools/Makefile.am
===================================================================
--- src/tools/Makefile.am (revision 2478)
+++ src/tools/Makefile.am (working copy)
@@ -11,11 +11,14 @@
 endif
 
 bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \
+ piv-tool \
  pkcs11-tool cardos-info eidenv \
  $(PROGRAMS_SSL)
 
 opensc_tool_SOURCES = opensc-tool.c util.c
 opensc_tool_LDADD = @GETOPTSRC@
+piv_tool_SOURCES = piv-tool.c util.c
+piv_tool_LDADD = @GETOPTSRC@
 opensc_explorer_SOURCES = opensc-explorer.c util.c
 opensc_explorer_LDADD = @GETOPTSRC@ @LIBREADLINE@
 pkcs15_tool_SOURCES = pkcs15-tool.c util.c
Index: src/libopensc/cards.h
===================================================================
--- src/libopensc/cards.h (revision 2478)
+++ src/libopensc/cards.h (working copy)
@@ -103,6 +103,13 @@
  SC_CARD_TYPE_BELPIC_BASE = 12000,
  SC_CARD_TYPE_BELPIC_GENERIC,
  SC_CARD_TYPE_BELPIC_EID,
+
+ /* PIV-II type cards */
+ SC_CARD_TYPE_PIV_II_BASE = 13000,
+ SC_CARD_TYPE_PIV_II_GENERIC,
+ SC_CARD_TYPE_PIV_II_OBERTHUR_BETA,
+ SC_CARD_TYPE_PIV_II_MSU_NO_CHAIN
+
 };
 
 #ifdef __cplusplus
Index: src/libopensc/card.c
===================================================================
--- src/libopensc/card.c (revision 2478)
+++ src/libopensc/card.c (working copy)
@@ -128,6 +128,8 @@
 
  return 0;
 }
+/** Builds TPDU and sends to the reader driver
+ */
 
 static int sc_transceive(sc_card_t *card, sc_apdu_t *apdu)
 {
@@ -161,6 +163,10 @@
  *data++ = apdu->p2;
  switch (apdu->cse) {
  case SC_APDU_CASE_1:
+ if (card->slot->active_protocol == SC_PROTO_T0)
+ /* TO adds an additional 0x00 byte to the TPDU */
+ *data++;
+ break;
  break;
  case SC_APDU_CASE_2_SHORT:
  *data++ = (u8) apdu->le;
@@ -185,10 +191,13 @@
  data += data_bytes;
  /* in case of T0 the Le value is omitted  */
  if (card->slot->active_protocol == SC_PROTO_T1) {
- if (apdu->le == 256)
- *data++ = 0x00;
- else
- *data++ = (u8) apdu->le;
+// if (apdu->le == 256)
+// *data++ = 0x00;
+// else
+// *data++ = (u8) apdu->le;
+ if (card->slot->active_protocol != SC_PROTO_T0)
+ /* unless T0 is used add Le byte */
+ *data++ = (u8) (apdu->le & 0xff);
  }
  break;
  }
Index: src/libopensc/iso7816.c
===================================================================
--- src/libopensc/iso7816.c (revision 2478)
+++ src/libopensc/iso7816.c (working copy)
@@ -584,8 +584,8 @@
  }
 
  orig_apdu->resplen = apdu.resplen;
- orig_apdu->sw1 = 0x90;
- orig_apdu->sw2 = 0x00;
+ orig_apdu->sw1 = apdu.sw1;
+ orig_apdu->sw2 = apdu.sw2;
 
  SC_FUNC_RETURN(card->ctx, 3, apdu.resplen);
 }
Index: src/libopensc/ctx.c
===================================================================
--- src/libopensc/ctx.c (revision 2478)
+++ src/libopensc/ctx.c (working copy)
@@ -72,6 +72,7 @@
  { "jcop", (void *) sc_get_jcop_driver },
 #ifdef HAVE_OPENSSL
  { "oberthur", (void *) sc_get_oberthur_driver },
+ { "PIV-II",(void *) sc_get_piv_driver },
 #endif
  { "belpic", (void *) sc_get_belpic_driver },
  { "atrust-acos", (void *)sc_get_atrust_acos_driver },
Index: src/libopensc/pkcs15-syn.c
===================================================================
--- src/libopensc/pkcs15-syn.c (revision 2478)
+++ src/libopensc/pkcs15-syn.c (working copy)
@@ -40,6 +40,8 @@
  sc_pkcs15emu_opt_t *);
 extern int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *,
  sc_pkcs15emu_opt_t *);
+extern int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card,
+ sc_pkcs15emu_opt_t *opts);
 extern int sc_pkcs15emu_gemsafe_init_ex(sc_pkcs15_card_t *p15card,
  sc_pkcs15emu_opt_t *opts);
 extern int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card,
@@ -57,6 +59,7 @@
  { "netkey", sc_pkcs15emu_netkey_init_ex },
  { "esteid", sc_pkcs15emu_esteid_init_ex },
  { "postecert", sc_pkcs15emu_postecert_init_ex  },
+ { "PIV-II", sc_pkcs15emu_piv_init_ex },
  { "gemsafe", sc_pkcs15emu_gemsafe_init_ex },
  { "actalis", sc_pkcs15emu_actalis_init_ex },
  { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex},
@@ -359,6 +362,10 @@
  df_type = SC_PKCS15_CDF;
  data_len = sizeof(struct sc_pkcs15_cert_info);
  break;
+ case SC_PKCS15_TYPE_DATA_OBJECT:
+ df_type = SC_PKCS15_DODF;
+ data_len = sizeof(struct sc_pkcs15_data_info);
+ break;
  default:
  sc_error(p15card->card->ctx,
  "Unknown PKCS15 object type %d\n", type);
Index: src/libopensc/Makefile.am
===================================================================
--- src/libopensc/Makefile.am (revision 2478)
+++ src/libopensc/Makefile.am (working copy)
@@ -28,7 +28,9 @@
  card-etoken.c card-tcos.c card-emv.c card-default.c \
  card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \
  card-oberthur.c card-belpic.c card-atrust-acos.c \
+ card-piv.c \
  \
+ pkcs15-piv.c \
  pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
  pkcs15-netkey.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafe.c \
  pkcs15-actalis.c pkcs15-atrust-acos.c
Index: src/libopensc/opensc.h
===================================================================
--- src/libopensc/opensc.h (revision 2478)
+++ src/libopensc/opensc.h (working copy)
@@ -919,6 +919,7 @@
 extern sc_card_driver_t *sc_get_oberthur_driver(void);
 extern sc_card_driver_t *sc_get_belpic_driver(void);
 extern sc_card_driver_t *sc_get_atrust_acos_driver(void);
+extern sc_card_driver_t *sc_get_piv_driver(void);
 
 #ifdef __cplusplus
 }

/*
 * card-default.c: Support for cards with no driver
 *
 * Copyright (C) 2001, 2002  Juha Yrjölä <[hidden email]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include "internal.h"
#include <string.h>
#include <fcntl.h>
#include <openssl/des.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/rsa.h>
#include "asn1.h"
#include "cardctl.h"

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 */
        int  eof;
        size_t max_recv_size; /* saved size, need to lie to pkcs15_read_file */
        size_t max_send_size;
};

struct piv_aid {
        int enumtag;
        int len;
        u8 *value;
};
 
/* The Generic entry should be the "A0 00 00 xx xx 00 00 10 00 "
 * But NIST has not published what xx xx  
 */
static struct piv_aid piv_aids[] = {
        {SC_CARD_TYPE_PIV_II_GENERIC,
                 9, (u8 *) "\xA0\x00\x00\x00\x01\x00\x00\x10\x00" },
        {SC_CARD_TYPE_PIV_II_OBERTHUR_BETA,
                11, (u8 *) "\xA0\x00\x00\x00\x77\x01\x00\x00\x07\x10\x00" },
        {SC_CARD_TYPE_PIV_II_MSU_NO_CHAIN,
                 7,  (u8 *) "\x11\x22\x33\x44\x55\x66\x77"},
        {0,  0, NULL }
};

enum {
        PIV_OBJ_CCC = 1,
        PIV_OBJ_CHUI,
        PIV_OBJ_X509_PIV_AUTH,
        PIV_OBJ_CHF1,
        PIV_OBJ_CHF2,
        PIV_OBJ_PI,
        PIV_OBJ_CHFI,
        PIV_OBJ_X509_DS,
        PIV_OBJ_X509_KM,
        PIV_OBJ_X509_CARD_AUTH,
        PIV_OBJ_SEC_OBJ,
        PIV_OBJ_9A03,
        PIV_OBJ_9B03,
        PIV_OBJ_9A06
};

struct piv_object {
        int enumtag;
        char * name;
        char * oidstring;
        int tag_len;
        u8  tag_value[3];
        u8  containerid[2];  /* will use as relative paths for simulation */
        int maxlen;
};

static 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", 266},
        { PIV_OBJ_CHUI, "Card Holder Unique Identifier",
                        "2.16.840.1.101.3.7.2.48.0", 3, "\x5F\xC1\x02", "\x30\x00", 3377},
        { 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", 1651+4} ,
        { PIV_OBJ_CHF1, "Card Holder Fingerprint I",
                        "2.16.840.1.101.3.7.2.96.16", 3, "\x5F\xC1\x03", "\x60\x01", 7768},
        { PIV_OBJ_CHF2, "Card Holder Fingerprint II",
                        "2.16.840.1.101.3.7.2.96.17", 3, "\x5F\xC1\x04", "\x60\x11", 7768},
        { PIV_OBJ_PI, "Printed Information",
                        "2.16.840.1.101.3.7.2.48.1", 3, "\x5F\xC1\x09", "\x30\x01", 106},
        { PIV_OBJ_CHFI, "Card Holder Facial Image",
                        "2.16.840.1.101.3.7.2.96.48", 3, "\x5F\xC1\x08", "\x60\x30", 12704},
        { 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", 1651},
        { 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", 1651},
        { 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", 1651},
        { PIV_OBJ_SEC_OBJ, "Security Object",
                        "2.16.840.1.101.3.7.2.144.0", 3, "\x5F\xC1\x06", "\x90\x00", 1000},
/* following not standard , to be used by piv-tool only for testing */
        { PIV_OBJ_9A03, "3DES-ECB AUTH",
                        "2.16.840.1.101.3.7.2.9999.2", 2, "\x9A\x03", "\x9A\x03", 24},
        { PIV_OBJ_9B03, "3DES-ECB ADM",
                        "2.16.840.1.101.3.7.2.9999.3", 2, "\x9B\x03", "\x9B\x03", 24},
        { PIV_OBJ_9A06, "Pub key from last genkey",
                        "2.16.840.1.101.3.7.2.9999.20", 2, "\x9A\x06", "\x9A\x06", 255},
        /* need other RSA types */
        { 0, "", "", 0, "", "", 0}
};
       
static struct sc_card_operations piv_ops;

static struct sc_card_driver piv_drv = {
        "PIV-II  for multiple cards",
        "piv",
        &piv_ops
};

/* If ptr == NULL, just return the size of the tag and lenght and data */
/* oterwise, store tag and length at **ptr, and increment */
static int put_tag_and_len(u8 tag, size_t len, u8 **ptr)
{
        int i;
        u8 *p;
       
        if (len <= 128) {
                i = 2;
        } else if (len < 256) {
                i = 3;
        } else {
                i = 4;
        }
       
        if (ptr) {
                p = *ptr;
                *p++ = tag;
                switch (i) {
                        case 2:
                                *p++ = len;
                                break;
                        case 3:
                                *p++ = 0x81;
                                *p++ = len;
                                break;
                        case 4:
                                *p++ = 0x82;
                                *p++ = (u8) (len >> 8);
                                *p++ = (u8) (len & 0xff);
                                break;
                }
                *ptr = p;
        } else {
                i += len;
        }
        return i;
}


static int piv_logout(sc_card_t * card)
{
// struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;

        SC_FUNC_CALLED(card->ctx,1);
        card->cache_valid = 0;
        /*
         * need to not call the iso7816 during sc_unlock, as select_file that may fail
         */
//DEE for now just return, we could set some bits to only do this at finish?
        return 0;
}


/* Add the PIV-II operations */
/* Should use our own keydata, actually should be common to all cards */
static int piv_generate_key(sc_card_t *card,
                struct sc_cardctl_cryptoflex_genkey_info *keydata)
{
// struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r,i;
        sc_apdu_t apdu;
        u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; /* should determine size based on operation */
        size_t taglen;
        const u8 *tag;
        u8 outdata[3];
       
        SC_FUNC_CALLED(card->ctx, 1);

        keydata->exponent = 0;
        keydata->pubkey = NULL;
        keydata->pubkey_len = 0;

        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] = 0x08; break;
                default:
                        SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
        }
        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x47, 0x00,
                keydata->key_num);
        apdu.lc = 3;
        apdu.data = outdata;
        apdu.datalen =  3;
        apdu.le = 255 /* sizeof(rbuf)*/ ;
        apdu.resp =rbuf;
        apdu.resplen = 255 /* sizeof(rbuf)*/;
               
        r = sc_transmit_apdu(card, &apdu);
        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
        SC_TEST_RET(card->ctx, r, "Card returned error");
       
        keydata->exponent = 0;
        tag = sc_asn1_find_tag(card->ctx, rbuf, apdu.resplen, 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, rbuf, apdu.resplen, 0x81, &taglen);

        if (tag != NULL && taglen > 0) {

                keydata->pubkey = malloc(taglen);
                if (keydata->pubkey == NULL)
                        SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY);
                keydata->pubkey_len = taglen;
                memcpy (keydata->pubkey, tag, taglen);
        }
       
        SC_FUNC_RETURN(card->ctx, 1, r);
}


/* find the PIV AID on the card. If card->type already filled in,
 * then look for specific AID only
 * Assumes that priv may not be present
 */

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;
        int aidindx = -1;

        SC_FUNC_CALLED(card->ctx,1);

        /* first  see if the default applcation will return an FCI
         * that we know about. Not striclky sp800-73 but is iso7816
         */

        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xA4, 0x00, 0x00);
        apdu.lc = 0;
        apdu.data = NULL;
        apdu.datalen = 0;
        apdu.resp = rbuf;
        apdu.resplen = sizeof(rbuf);
        apdu.le = 256;
        r = sc_transmit_apdu(card, &apdu);
        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
        if (r == 0 && apdu.resplen > 2 &&
                apdu.resp[0] == 0x6f &&
                apdu.resp[1] <= apdu.resplen - 2 ) {

                card->ops->process_fci(card, aid_file, apdu.resp+2, apdu.resp[1]);
                if (aid_file->name != NULL) {
                 
                        for (i = 0; piv_aids[i].len != 0; i++) {
                                if (aid_file->namelen >=  piv_aids[i].len &&
                                                memcmp(aid_file->name, piv_aids[i].value,
                                                piv_aids[i].len) == 0) {
                                        if (card->type > SC_CARD_TYPE_PIV_II_BASE &&
                                                card->type < SC_CARD_TYPE_PIV_II_BASE+1000 &&
                                                card->type == piv_aids[i].enumtag) {
                                                SC_FUNC_RETURN(card->ctx, 1, i);
                                        } else {
                                                SC_FUNC_RETURN(card->ctx, 1, i);
                                        }
                                }
                        }
                }
        }

        /* 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 != 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;
                apdu.data = piv_aids[i].value;
       
                apdu.datalen = apdu.lc;
                apdu.resp = rbuf;
                apdu.resplen = sizeof(rbuf);
                apdu.le = 256;

                r = sc_transmit_apdu(card, &apdu);
                SC_TEST_RET(card->ctx, r, "APDU transmit failed");

                r = sc_check_sw(card, apdu.sw1, apdu.sw2);

                if (r)
                        continue;

                if ( apdu.resplen == 0 && r == 0) {
                        /* could be the MSU card */
                        if (piv_aids[i].enumtag == SC_CARD_TYPE_PIV_II_MSU_NO_CHAIN) {
                                /* looks like MSU card */
                                sc_debug(card->ctx,"DEE MSU card without FCI");
                                SC_FUNC_RETURN(card->ctx, 1, i);
                        }
                        continue; /* other cards will return a FCI */
                }

                if (apdu.resp[0] != 0x6f || apdu.resp[1] > apdu.resplen - 2 )
                        SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NO_CARD_SUPPORT);
       
                card->ops->process_fci(card, aid_file, apdu.resp+2, apdu.resp[1]);
                if (aid_file->name == NULL)
                        SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NO_CARD_SUPPORT);

                SC_FUNC_RETURN(card->ctx, 1, i);
        }
       
        SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NO_CARD_SUPPORT);
}

#if 0

/* get response, get data may return with data and 61xx */
/* we will also do additional get response to read all the data */

static int piv_get_response(sc_card_t *card, sc_apdu_t *orig_apdu, size_t count)
{
        sc_apdu_t apdu;
        int r;

        sc_debug(card->ctx,"PIV_GET_RESPONSE ins = %02x\n resplen = %d",
                orig_apdu->ins, orig_apdu->resplen);

        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC0, 0x00, 0x00);
        apdu.le = count;
        apdu.resplen = count;
        apdu.resp = orig_apdu->resp + orig_apdu->resplen;

        r = sc_transmit_apdu(card, &apdu);
        SC_TEST_RET(card->ctx, r, "APDU transmit failed");
        if (apdu.resplen == 0)
                SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));

        if (apdu.resplen != count) {
                SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_WRONG_LENGTH);
        }

        orig_apdu->resplen += apdu.resplen;
        orig_apdu->sw1 = apdu.sw1;
        orig_apdu->sw2 = apdu.sw2;

        SC_FUNC_RETURN(card->ctx, 3, apdu.resplen);
}
#endif

/* the tag is the PIV_OBJ_*  */
/* DEE The sizes list in SP800-73 are advisory, so we should really try and
 * use the size from the tag in the first segment read from the card
 */
static int piv_get_data(sc_card_t * card, unsigned int enumtag,
                        u8 *buf, size_t buf_len)
{
        struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        sc_apdu_t apdu;
        u8 *p, *rp;
        u8 *buf_end;
        int r = 0;
        size_t chunck;
        u8 tagbuf[8];
        int tag_len;
               
        SC_FUNC_CALLED(card->ctx,1);
        sc_debug(card->ctx, "get_data: tag=%d size=%d\n", enumtag, buf_len);

        tag_len = piv_objects[enumtag].tag_len;

        p = tagbuf;
        put_tag_and_len(0x5c, tag_len, &p);
        memcpy(p, piv_objects[enumtag].tag_value, tag_len);
        p += tag_len;

        rp = buf;
        buf_end = buf + buf_len;

        sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xCB, 0x3F, 0xFF);
        apdu.lc = p - tagbuf;
        apdu.data = tagbuf;
        apdu.datalen = p - tagbuf;
        apdu.le =  (buf_len < 256) ? buf_len : 256;
        apdu.resplen =  buf_len;
        apdu.resp = rp;
       
    /*
         * the Beta PIV card will only recover the public key during a generate
         * key operation. If the piv-tool was used it would save this
         * as an OpenSSL EVP_KEY PEM using the -o parameter
         * we will look to see there as a file and load it
         * this is ugly, and maybe the pkcs15 cache would work
         * but we only need it  to get the OpenSSL req with engine to work.
         * and then only for the first beta card.
         */
        if (piv_objects[enumtag].enumtag == PIV_OBJ_9A06)  {
                BIO * bp = NULL;
                RSA * rsa = NULL;
                u8 *p;
                size_t derlen;
                size_t taglen;
                char * keyfilename = NULL;

                keyfilename = getenv("PIV_9A06_KEY");

                if (keyfilename == NULL) {
                        r = SC_ERROR_FILE_NOT_FOUND;
                        goto err;
                }
        bp = BIO_new(BIO_s_file());
        if (BIO_read_filename(bp, keyfilename) <= 0) {
            BIO_free(bp);
                        r = SC_ERROR_FILE_NOT_FOUND;
                        goto err;
        }
        rsa = PEM_read_bio_RSAPublicKey(bp, &rsa, NULL, NULL);
        BIO_free(bp);
        if (!rsa) {
            sc_debug(card->ctx,"Unable to load the public key");
                        r =  SC_ERROR_DATA_OBJECT_NOT_FOUND;
                        goto err;
        }


        derlen = i2d_RSAPublicKey(rsa, NULL);
        if (derlen <= 0) {
                        r =  SC_ERROR_DATA_OBJECT_NOT_FOUND;
                        goto err;
                }
                p = buf;
                taglen = put_tag_and_len(0x99, derlen, NULL);

                put_tag_and_len(0x53, taglen, &p);
                put_tag_and_len(0x99, derlen, &p);

        i2d_RSAPublicKey(rsa, &p);
     
                apdu.resplen =  p - buf;
        RSA_free(rsa);
       
                priv->eof = 1;

        /* end of the Beta card hack */
        } else {
                card->max_recv_size = priv->max_recv_size;
sc_debug(card->ctx,"get_data first APDU\n");
                r = sc_transmit_apdu(card, &apdu);
                card->max_recv_size = 0xffff;
                if (r < 0) {
                        sc_debug(card->ctx, "APDU transmit failed\n");
                        goto err;
                }
                rp += apdu.resplen;

if ( apdu.resplen == 0 ) {
        sc_debug(card->ctx,"data maybe correct, %02x:%02x:%02x:%02x:...%02x%02x",
                        rp[0],rp[1],rp[2],rp[3], rp[256], rp[257]);
}

/* loop reading the rest of the file */
                while  (r >= 0 && apdu.sw1 == 0x61) {
                       
                        sc_debug(card->ctx,"get_data checking r=%d resplen=%d SW1=%02x SW2=%02x\n",
                                r, apdu.resplen, apdu.sw1, apdu.sw2);

                        chunck = apdu.sw2 ? apdu.sw2 : 256;
                        chunck = (chunck < 256) ? chunck: 256;
                        chunck = (chunck > priv->max_recv_size) ? priv->max_recv_size : chunck;

                        sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xC0, 0x00, 0x00);
                        apdu.le = chunck;
                        apdu.resplen = buf_end - rp;
                        apdu.resp = rp;
                        r = sc_transmit_apdu(card, &apdu);
                        if (r >= 0) {
                                rp += apdu.resplen;
                        }
                }
                sc_debug(card->ctx,"r = %d  resplen=%d SW1=0x%02x SW2=0x%02x\n",
                        r, apdu.resplen, apdu.sw1, apdu.sw2);

                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
                if (r < 0) {
                        sc_debug(card->ctx, "Card returned error ");
                        goto err;
                }
                if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00)
                        priv->eof = 1;
                apdu.resplen = rp - buf;
        }

err:
        if (r >=0)
                r = apdu.resplen;
        SC_FUNC_RETURN(card->ctx, 1, r);
}
         
/*
 * Callers of this are expecting a file without tags,
 * So we need to know what type of file this is, so we can get the
 * correct object out if the taged data returned by get_data
 */
static int piv_read_binary(sc_card_t *card, unsigned int idx,
                unsigned char *buf, size_t count, unsigned long flags)
{
        struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r;
        u8 *rbuf;
        size_t rbuflen;
        u8 *tag;
        size_t taglen;
        u8 *body;
        size_t bodylen;

        SC_FUNC_CALLED(card->ctx,1);
        if (priv->selected_obj < 0)
                 SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);

        if (priv->eof == 1)
                return 0;

        rbuflen = piv_objects[priv->selected_obj].maxlen;
        rbuf = (u8*) malloc(rbuflen);
        if (rbuf == NULL)
                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY);
       

        r = piv_get_data(card, priv->selected_obj, rbuf, rbuflen);
       
        if (r >=0) {
                rbuflen = r;
          body = sc_asn1_find_tag(card->ctx, (const u8*) rbuf, rbuflen, 0x53, &bodylen);
                if (body == NULL) {
                        /* if missing, assume its the body */
                        /* DEE bug in the beta card */
                        sc_debug(card->ctx," ***** tag 0x53 MISSING \n");
                        body =  rbuf;
                        bodylen = rbuflen;
// r = SC_ERROR_INVALID_DATA;
// goto err;
                }
                switch (piv_objects[priv->selected_obj].enumtag) {
                        case PIV_OBJ_X509_PIV_AUTH:
                        case PIV_OBJ_X509_DS:
                        case PIV_OBJ_X509_KM:
                        case PIV_OBJ_X509_CARD_AUTH:
                                /* get the certificate out */
                                tag = sc_asn1_find_tag(card->ctx, (const u8*) body,  bodylen, 0x70, &taglen);
                                if (tag == NULL) {
                                        r = SC_ERROR_OBJECT_NOT_VALID;
                                        break;
                                }
                                memcpy(buf, tag, taglen); /*DEE should check lengths */
                                r = taglen;
                                tag = sc_asn1_find_tag(card->ctx, (const u8*) body,  bodylen, 0x71, &taglen);
                                if (tag && ((*tag) & 0x80)) {
                                        sc_debug(card->ctx,0,"Cert is gziped! %0x2.2x\n",*tag);
                                }
                                break;
                        default:
                                /* For now get the first tag, and return the data */
                                tag = sc_asn1_find_tag(card->ctx, (const u8*) body,  bodylen, *body, &taglen);
                                if (tag == NULL) {
                                        r = SC_ERROR_OBJECT_NOT_VALID;
                                        break;
                                }
                                memcpy(buf, tag, taglen); /*DEE should check lengths */
                                r = taglen;
                                break;
                }
        }
err:
        if (rbuf)
                free(rbuf);
        SC_FUNC_RETURN(card->ctx, 1, r);
}

 
/* 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, unsigned int tag,
                const u8 *buf, size_t buf_len)
{
        sc_apdu_t apdu;
// struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r;
        size_t  segoff, seglen, totoff;
        u8 * sbuf;
        size_t sbuflen;
        u8 rbuf[8]; /* should not need this */
        u8 * p;
        int tag_len;

        SC_FUNC_CALLED(card->ctx,1);

        tag_len = piv_objects[tag].tag_len;
        sbuflen = put_tag_and_len(0x5c, tag_len, NULL) + buf_len;
         if (!(sbuf = (u8 *) malloc(sbuflen)))
                return 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;

        segoff = 0;
        totoff = p - sbuf;

        while (segoff < totoff) {
                seglen = totoff - segoff;
                sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xDB, 0x3F, 0xFF);
                if (seglen > 255) { /* see Oberthur docs */
                        seglen = 255;
                        apdu.cla |= 0x10;  /* chain */
                }
                apdu.lc = seglen;
                apdu.data = sbuf + segoff;
                apdu.datalen = seglen;
                apdu.le = 2;
                apdu.resplen = 2;
                apdu.resp = rbuf;
                r = sc_transmit_apdu(card, &apdu);
                if (r < 0)
                        break;
                r = sc_check_sw(card, apdu.sw1, apdu.sw2);
                if (r < 0)
                        break;
                segoff = segoff + seglen;
        }

        if (sbuf)
                free(sbuf);
        SC_FUNC_RETURN(card->ctx, 1, r);
}


/*
 * We need to add the  0x53 tag and other specific tags,
 * and call the piv_put_data
 */

static int piv_write_binary(sc_card_t *card, unsigned int idx,
                const u8 *buf, size_t count, unsigned long flags)
{
        struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r = 0;
        u8 *sbuf = NULL;
        u8 *p;
        size_t sbuflen;
        size_t taglen;
       
        SC_FUNC_CALLED(card->ctx,1);

        if (priv->selected_obj < 0)
                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
        if (idx != 0)
                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NO_CARD_SUPPORT);
       
        switch (piv_objects[priv->selected_obj].enumtag) {
                case PIV_OBJ_X509_PIV_AUTH:
                case PIV_OBJ_X509_DS:
                case PIV_OBJ_X509_KM:
                case PIV_OBJ_X509_CARD_AUTH:
               
                        taglen = put_tag_and_len(0x70, count, NULL)
                                + put_tag_and_len(0x71, 1, NULL);
                        sbuflen = 1 + taglen; /*  1 is for the 0x53 see below */

                        sbuf = (u8*) malloc(sbuflen);
                        if (sbuf == NULL)
                                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY);
                        p = sbuf;
/* beta says tag is 53, but lng is absent! */
// put_tag_and_len(0x53, taglen, &p);
                        *p++ = 0x53;
                        put_tag_and_len(0x70, count, &p);
                        memcpy(p, buf, count);
                        p += count;
                        put_tag_and_len(0x71, 1, &p);
                        *p++ = 0x00; /* certinfo, i.e. not gziped */
                        break;
                       
                default:
                sc_debug(card->ctx, "Don't know how to write object %s\n",
                        piv_objects[priv->selected_obj].name);
                        r = SC_ERROR_NOT_SUPPORTED;
                        break;
        }

        if (r == 0)
        r = piv_put_data(card, priv->selected_obj, sbuf, sbuflen);
        if (sbuf)
                free(sbuf);

        SC_FUNC_RETURN(card->ctx, 1, r);
}


static int piv_general_authenticate(sc_card_t *card,
                                        const u8 * sbuf, size_t sbuflen,
                                        u8 * rbuf, size_t rbuflen,
                                        int alg, int keyref)
{
// struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r;
        sc_apdu_t apdu;

        SC_FUNC_CALLED(card->ctx,1);

        sc_debug(card->ctx, "piv_general_authenticate %d %d %d %d\n",
                sbuflen,  rbuflen, alg, keyref);

        sc_format_apdu(card, &apdu,
                        rbuflen > 0 ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT,
                        0x87, alg, keyref);
        apdu.lc = sbuflen;
        apdu.datalen = sbuflen;
        apdu.data = sbuf;
        if (rbuflen > 0 ) {
                apdu.resp = rbuf;
                apdu.le = rbuflen;
                apdu.resplen = rbuflen;
        } else if (rbuflen < 0) {
                 apdu.resp = rbuf;
                 apdu.le = 0;
                 apdu.resplen = -rbuflen;
        }
               
       
        r = sc_transmit_apdu(card, &apdu);
       
        r = sc_check_sw(card, apdu.sw1, apdu.sw2);
        if (r != 0) {
                if (apdu.resplen > 0 && *apdu.resp == 0x7c ) {
                /* need to check for 62 82 as we gave it a bigger buf that needed */
                sc_debug(card->ctx, "ASSUMING DATA RETURNED CORRECTLY, resplen=%d, sw1=0x%02x sw2=0x%02x resp=%02x:%02x:%02x:%02x\n",
                                apdu.resplen, apdu.sw1, apdu.sw2,
                                apdu.resp[0], apdu.resp[1], apdu.resp[2], apdu.resp[3]);
                r = 0;
                }
        }

        if (r == 0) {
                r = apdu.resplen;
        }
        SC_FUNC_RETURN(card->ctx, 1, r);
}


/*
 * This is strickly for test and use with the Beta cards,
 * There needs to be a beter way to get the key
 * But it is only needed during initialization/personalization of the card
 */
static int piv_general_external_authenticate(sc_card_t *card)
{
        struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r;
        u8 rbuf[255];
        u8 sbuf[255];
        u8 *p, *q;
        int f = -1;
        char keybuf[24*3-1];  /* 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;
       
        DES_cblock k1, k2, k3;
        DES_key_schedule ks1, ks2, ks3;

        SC_FUNC_CALLED(card->ctx,1);

        keyfilename = (char *)getenv("PIV_9B03_KEY");
        if (keyfilename == NULL) {
                sc_debug(card->ctx,
                        "Unable to get PIV_EXT_AUTH_KEY=filename for general_external_authenticate\n");
                r = SC_ERROR_FILE_NOT_FOUND;
                goto err;
        }
        if ((f = open(keyfilename, O_RDONLY)) < 0) {
                sc_debug(card->ctx," Unable to load 3des key for general_external_authenticate\n");
                r = SC_ERROR_FILE_NOT_FOUND;
                goto err;
        }
        if (read(f, keybuf, 71) != 71) {
                sc_debug(card->ctx," Unable to read 3des key for general_external_authenticate\n");
                r = SC_ERROR_WRONG_LENGTH;  
                goto err;
        }
        keybuf[23] = '\0';
        keybuf[47] = '\0';
        outlen = 8;
        r = sc_hex_to_bin(keybuf, k1, &outlen);
        if (r) goto err;
        outlen = 8;
        r = sc_hex_to_bin(keybuf+24, k2, &outlen);
        if (r) goto err;
        outlen = 8;
        r = sc_hex_to_bin(keybuf+48, k3, &outlen);
        if (r) goto err;
       
        SC_FUNC_CALLED(card->ctx,1);

        p = sbuf;
        q = rbuf;
        *p++ = 0x7C;
        *p++ = 0x02;
        *p++ = 0x81;
        *p++ = 0x00;

        r = piv_general_authenticate(card, sbuf, p - sbuf,
                        rbuf, 12,
                        0x00, 0x00);
  if (r < 0) goto err;
        q = rbuf;
        if ( (*q++ != 0x7C)
                || (*q++ != r - 2)
                || (*q++ != 0x81)
                || (*q++ != r - 4)) {
                r =  SC_ERROR_INVALID_DATA;
                goto err;
        }

        p = sbuf;
        *p++ = 0x7c;
        *p++ = rbuf[1];
        *p++ = 0x82;
        *p++ = rbuf[3];
       
        DES_set_key((DES_cblock *) &k1, &ks1);
        DES_set_key((DES_cblock *) &k2, &ks2);
        DES_set_key((DES_cblock *) &k3, &ks3);
       
        /* MSU applet uses single des only, so only use ks1 */
        if ((priv-> enumtag == SC_CARD_TYPE_PIV_II_MSU_NO_CHAIN))
                DES_ecb_encrypt(q, p, &ks1, DES_ENCRYPT);
        else
                DES_ecb3_encrypt(q, p, &ks1, &ks2, &ks3, DES_ENCRYPT);
        p += rbuf[3];
       
        r = piv_general_authenticate(card, sbuf, p - sbuf,
                        rbuf, 12,
                        (priv-> enumtag == SC_CARD_TYPE_PIV_II_MSU_NO_CHAIN)? 0x01:0x03,
                         0x9b);

err:
        if (f >=0)
                close(f);
        SC_FUNC_RETURN(card->ctx, 1, r);
}


static int piv_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
{
        switch(cmd) {
                case SC_CARDCTL_LIFECYCLE_SET:
                        return piv_general_external_authenticate(card);
                        break;
                       
                case SC_CARDCTL_CRYPTOFLEX_GENERATE_KEY:
                        return piv_generate_key(card,
                                (struct sc_cardctl_cryptoflex_genkey_info *) ptr);
                        break;
        }

        return SC_ERROR_NOT_SUPPORTED;
}


/* piv does not need set|restore_sec_env */
static int piv_set_security_env(sc_card_t *card,
                    const sc_security_env_t *env,
                    int se_num)
{
        SC_FUNC_CALLED(card->ctx,1);
        return 0;
}


static int piv_restore_security_env(sc_card_t *card, int se_num)
{
        SC_FUNC_CALLED(card->ctx,1);
        return 0;
}


static int piv_compute_signature(sc_card_t *card,
                                        const u8 * data, size_t datalen,
                                        u8 * out, size_t outlen)
{
// struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int r;
        u8 *p;
        u8 *tag;
        size_t taglen;
        u8 *body;
        size_t bodylen;

        u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
        u8 rbuf[SC_MAX_APDU_BUFFER_SIZE];
        size_t rbuflen;
       
        SC_FUNC_CALLED(card->ctx,1);

        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);

        memcpy(p, data, datalen);
        p += datalen;

        r = piv_general_authenticate(card, sbuf, p - sbuf,
                        rbuf, 240 /*DEE needs work to get the correct size SC_MAX_APDU_BUFFER_SIZE*/,
                        0x06, 0x9a); /*DEE need to use other alg and keyref */

        if ( r >= 0) {
                rbuflen = r;
          body = sc_asn1_find_tag(card->ctx, (const u8*) rbuf, rbuflen, 0x7c, &bodylen);
                if (body) {
                        tag = sc_asn1_find_tag(card->ctx, (const u8*) body,  bodylen, 0x82, &taglen);
                        memcpy(out, tag, taglen);
                        r = taglen;
                } else
                        r = SC_ERROR_INVALID_DATA;
        }      

        SC_FUNC_RETURN(card->ctx, 1, r);
}


static int piv_find_obj_by_containerid(sc_card_t *card, u8 * str)
{
        int i;

        SC_FUNC_CALLED(card->ctx,4)
        sc_debug(card->ctx, "str=0x%02X%02X\n", str[0], str[1]);

        for (i = 0; piv_objects[i].enumtag; i++) {
                if ( str[0] == piv_objects[i].containerid[0]
                        && str[1] == piv_objects[i].containerid[1])
                        SC_FUNC_RETURN(card->ctx, 4, i);
        }
        SC_FUNC_RETURN(card->ctx, 4, -1);
}


/*
 * 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
 * We can use this to determine the type of data requested, like a cert
 * or pub key.
 */
static int piv_select_file(sc_card_t *card,
                                        const sc_path_t *in_path,
                                        sc_file_t **file_out)
{
  struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;
        int i;
        u8 *path;
        int pathlen;
        sc_file_t *file = NULL;
       
        SC_FUNC_CALLED(card->ctx,1)

        path = in_path->value;
        pathlen = in_path->len;
       
        /* only support single EF in current application */

        if (pathlen > 2 && memcmp(path, "\x3F\x00", 2) == 0) {
                path += 2;
                pathlen -= 2;
        }
        if (pathlen != 2)
                        return SC_ERROR_INVALID_ARGUMENTS;
         
        i = piv_find_obj_by_containerid(card, path);

        if (i< 0)
                SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_FILE_NOT_FOUND);
        priv->selected_obj = i;
        priv->eof = 0;

        /* make it look like the file was found. We don't want to read it now,. */

        if (file_out) {
                file = sc_file_new();
                if (file == NULL)
                        SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY);

                file->path = *in_path;
                /* this could be like the FCI */
                file->type =  SC_FILE_TYPE_DF;
                file->shareable = 0;
                file->ef_structure = 0;
                file->size = piv_objects[i].maxlen;
                file->id = (piv_objects[i].containerid[0]<<8) + piv_objects[i].containerid[1];

                *file_out = file;
        }

        SC_FUNC_RETURN(card->ctx, 1, 0);

}


static int piv_finish(sc_card_t *card)
{
  struct piv_private_data * priv = (struct piv_private_data *) card->drv_data;

        SC_FUNC_CALLED(card->ctx,1);
        if (priv) {
                if (priv->aid_file)
                        sc_file_free(priv->aid_file);
                free(priv);
        }
        return 0;
}


static int piv_match_card(sc_card_t *card)
{
        SC_FUNC_CALLED(card->ctx,1);
        /* dee need to look at AID */
        return 1; /* always match */
}


static int piv_init(sc_card_t *card)
{
        int r;
         unsigned long flags;
        struct piv_private_data *priv;

         SC_FUNC_CALLED(card->ctx,1);
        priv = (struct piv_private_data *) malloc(sizeof(struct piv_private_data));

        if (!priv)
                return SC_ERROR_OUT_OF_MEMORY;
        memset(priv, 0, sizeof(struct piv_private_data));
        priv->aid_file = sc_file_new();
        priv->selected_obj = -1;
        priv->max_recv_size = card->max_recv_size;
        priv->max_send_size = card->max_send_size;
        card->max_recv_size = 0xffff; /* pkcs15 read_file has problems. */
        card->max_send_size = 0xffff;
       
        card->drv_data = priv;
        card->cla = 0x00;
        card->name = "PIV-II card";

        r = piv_find_aid(card, priv->aid_file);
        if (r < 0) {
                 sc_error(card->ctx, "Failed to initialize %s\n", card->name);
        }
        priv->enumtag = piv_aids[r].enumtag;
        card->type = piv_aids[r].enumtag;

        flags = SC_ALGORITHM_RSA_PAD_PKCS1 | SC_ALGORITHM_ONBOARD_KEY_GEN;
        _sc_card_add_rsa_alg(card,1024, flags, 0);
        card->caps |= SC_CARD_CAP_RNG;

        if (r > 0)
                r = 0;
        SC_FUNC_RETURN(card->ctx, 1, r);
}


static struct sc_card_driver * sc_get_driver(void)
{
        struct sc_card_driver *iso_drv = sc_get_iso7816_driver();

        piv_ops = *iso_drv->ops;
        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.logout = piv_logout;
// piv_ops.get_response = piv_get_response;
        piv_ops.read_binary = piv_read_binary;
        piv_ops.write_binary = piv_write_binary;
        piv_ops.set_security_env = piv_set_security_env;
        piv_ops.restore_security_env = piv_restore_security_env;
        piv_ops.compute_signature = piv_compute_signature;
        piv_ops.card_ctl = piv_card_ctl;

        return &piv_drv;
}


#if 1
struct sc_card_driver * sc_get_piv_driver(void)
{
        return sc_get_driver();
}
#endif


/*
 * partial PKCS15 emulation for PIV-II cards
 * only minimal use of the authentication cert and key
 *
 * Copyright (C) 2005, Douglas E. Engert <[hidden email]>
 *               2004, Nils Larsch <[hidden email]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#include <opensc/pkcs15.h>
#include <opensc/log.h>
#include <opensc/cardctl.h>
#include <opensc/cards.h>
#include "../tools/util.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <openssl/bio.h>
#include <openssl/rsa.h>
#include <openssl/pem.h>

#define MANU_ID "piv_II "

int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *);


typedef struct objdata_st {
        const char *id;
        const char *label;
        char *aoid;
        int     authority;
        const char *path;
        int         obj_flags;
} objdata;

typedef struct cdata_st {
        const char *id;
        const char *label;
        int    authority;
        const char *path;
        int         obj_flags;
} cdata;

typedef struct pdata_st {
        const char *id;
        const char *label;
        const char *path;
        int         ref;
        int         type;
        unsigned int maxlen;
        unsigned int minlen;
        unsigned int storedlen;
        int         flags;
        int         tries_left;
        const 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;
        u8 * der;
        size_t derlen;
} pubdata;

typedef struct prdata_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;
} prdata;

typedef struct keyinfo_st {
        int fileid;
        sc_pkcs15_id_t id;
        unsigned int modulus_len;
        u8 modulus[1024/8];
} keyinfo;

       

#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION
#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \
                        SC_PKCS15_PRKEY_USAGE_DECRYPT | \
                        SC_PKCS15_PRKEY_USAGE_WRAP    | \
                        SC_PKCS15_PRKEY_USAGE_UNWRAP
#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \
                        SC_PKCS15_PRKEY_USAGE_DECRYPT | \
                        SC_PKCS15_PRKEY_USAGE_WRAP    | \
                        SC_PKCS15_PRKEY_USAGE_UNWRAP  | \
                        SC_PKCS15_PRKEY_USAGE_SIGN



/* copied from util.c to get around link problems in tests etc */
static int
piv_parse_application_id(struct sc_object_id *oid, char *oid_str)
{
        int ii, ret = SC_ERROR_INVALID_ARGUMENTS;
        char *p, *q;

        if (!oid)
                return ret;
        /* init oid */
        for (ii=0; ii<SC_MAX_OBJECT_ID_OCTETS; ii++)
                oid->value[ii] = -1;

        if (!(p = oid_str))
                return ret;
       
        for (ii=0; ii < SC_MAX_OBJECT_ID_OCTETS; ii++)   {
                oid->value[ii] = strtol(p, &q, 10);
                if (!*q)
                        break;
                if (!(q[0] == '.' && isdigit(q[1]))) {
                        return ret;
                }
                p = q + 1;
        }

        return SC_SUCCESS;
}


static int piv_detect_card(sc_pkcs15_card_t *p15card)
{
        sc_card_t *card = p15card->card;

        SC_FUNC_CALLED(card->ctx, 1);
        if (card->type < SC_CARD_TYPE_PIV_II_GENERIC
                || card->type >= SC_CARD_TYPE_PIV_II_GENERIC+1000)
                return SC_ERROR_INVALID_CARD;
        return SC_SUCCESS;
}

static int sc_pkcs15emu_piv_init(sc_pkcs15_card_t *p15card)
{
        const objdata objects[] = {
                {"1", "Card Capability Container",
                                "2.16.840.1.101.3.7.1.219.0", 0, "DB00", 0},
                {"2", "Card Holder Unique Identifier",
                                "2.16.840.1.101.3.7.2.48.0", 0 , "3000", 0},
                {"3", "Card Holder Fingerprint I",
                                "2.16.840.1.101.3.7.2.96.16", 1, "6010", 0},
                {"4", "Card Holder Fingerprint II",
                                "2.16.840.1.101.3.7.2.96.17", 1, "6011", 0},
                {"5", "Printed Information",
                                "2.16.840.1.101.3.7.2.48.1", 1, "3001", 0},
                {"6", "Card Holder Facial Image",
                                "2.16.840.1.101.3.7.2.96.48", 1, "6030", 0},
                {"7", "Security Object",
                                "2.16.840.1.101.3.7.2.144.0", 0, "9000", 0},
                {NULL, NULL, NULL, 0, NULL, 0}
        };

        const cdata certs[] = {
                {"1", "Certificate for PIV Authentication",
                                0, "0101", SC_PKCS15_CO_FLAG_MODIFIABLE},
                {"2", "Certificate for Digital Signature",
                                0, "0100", SC_PKCS15_CO_FLAG_MODIFIABLE},
                {"3", "Certificate for Key Management",
                                0, "0102", SC_PKCS15_CO_FLAG_MODIFIABLE},
                {"4", "Certificate for Card Authentication",
                                0, "0500", SC_PKCS15_CO_FLAG_MODIFIABLE},
                {NULL, NULL, 0, NULL, 0}
        };

        const pindata pins[] = {
                { "1", "Card Holder PIV pin", "", 0x80,
                  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
                  8, 4, 8,
                  SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
                  SC_PKCS15_PIN_FLAG_LOCAL,
                  -1, 0xFF,
                  SC_PKCS15_CO_FLAG_PRIVATE },
                { "2", "PIV Admin Key", "", 0x81,
                  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
                  8, 4, 8,
                  SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
                  SC_PKCS15_PIN_FLAG_LOCAL | SC_PKCS15_PIN_FLAG_SO_PIN |
                  SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN,
                  -1, 0xFF,
                  SC_PKCS15_CO_FLAG_PRIVATE },
                /* there are some more key, but dont need for now */
                { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
        };

        const pubdata pubkeys[] = {

                { "1", "AUTH pubkey", 1024, USAGE_AUT, "9A06",
                  0x9A, "1", SC_PKCS15_CO_FLAG_PRIVATE},
                { NULL, NULL, 0, 0, NULL, 0, NULL, 0}
               
        };

        const prdata prkeys[] = {
                { "1", "AUTH key", 1024, USAGE_AUT, "",
                  0x9A, "1", SC_ALGORITHM_RSA_PAD_PKCS1 | SC_PKCS15_CO_FLAG_PRIVATE},
                { NULL, NULL, 0, 0, NULL, 0, NULL, 0}
        };

        int    r, i;
        int dfpath;
        sc_card_t *card = p15card->card;

        SC_FUNC_CALLED(card->ctx, 1);

        /* could read this off card if needed */

        p15card->label = strdup("PIV-II");
        p15card->manufacturer_id = strdup(MANU_ID);
        /* get serial number */
//        r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
//        r = sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
//        if (r != SC_SUCCESS)
//                return SC_ERROR_INTERNAL;
//        p15card->serial_number = strdup(buf);
        p15card->serial_number = strdup("9876543210");

        sc_debug(card->ctx, "PIV-II");

        /* set other objects */
        for (i = 0; objects[i].label; i++) {
                struct sc_pkcs15_data_info obj_info;
                struct sc_pkcs15_object    obj_obj;

                memset(&obj_info, 0, sizeof(obj_info));
                memset(&obj_obj, 0, sizeof(obj_obj));
                sc_pkcs15_format_id(objects[i].id, &obj_info.id);
                sc_format_path(objects[i].path, &obj_info.path);
                strncpy(obj_info.app_label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
                piv_parse_application_id(&obj_info.app_oid, objects[i].aoid);

                strncpy(obj_obj.label, objects[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
                obj_obj.flags = objects[i].obj_flags;
               
                r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT,
                        &obj_obj, &obj_info);
                if (r < 0)
                        SC_FUNC_RETURN(card->ctx, 1, r);
        }

        /* set certs */
        for (i = 0; certs[i].label; i++) {
                struct sc_pkcs15_cert_info cert_info;
                struct sc_pkcs15_object    cert_obj;
               
                memset(&cert_info, 0, sizeof(cert_info));
                memset(&cert_obj,  0, sizeof(cert_obj));
       
                sc_pkcs15_format_id(certs[i].id, &cert_info.id);
                cert_info.authority = certs[i].authority;
                sc_format_path(certs[i].path, &cert_info.path);

                strncpy(cert_obj.label, certs[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
                cert_obj.flags = certs[i].obj_flags;

                r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
                if (r < 0)
                        SC_FUNC_RETURN(card->ctx, 1, r);
        }


        /* set pins */

        for (i = 0; pins[i].label; i++) {
                struct sc_pkcs15_pin_info pin_info;
                struct sc_pkcs15_object   pin_obj;

                memset(&pin_info, 0, sizeof(pin_info));
                memset(&pin_obj,  0, sizeof(pin_obj));

                sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
                pin_info.reference     = pins[i].ref;
                pin_info.flags         = pins[i].flags;
                pin_info.type          = pins[i].type;
                pin_info.min_length    = pins[i].minlen;
                pin_info.stored_length = pins[i].storedlen;
                pin_info.max_length    = pins[i].maxlen;
                pin_info.pad_char      = pins[i].pad_char;
                sc_format_path(pins[i].path, &pin_info.path);
                pin_info.path.value[2] = dfpath >> 8;
                pin_info.path.value[3] = dfpath & 0xff;
                pin_info.tries_left    = -1;

                strncpy(pin_obj.label, pins[i].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);
                if (r < 0)
                        SC_FUNC_RETURN(card->ctx, 1, r);
        }

        /* needs work, as we may want to add more then one key */

        sc_debug(card->ctx, "PIV-II");

        /* set public keys */
        /* We may only need this during initialzation when genkey
         * gets the pubkey, but it can not be read from the card
         * at a later time. May need to stash in a file
         */

        for (i = 0; pubkeys[i].label; i++) {
                BIO * bp = NULL;
                RSA * rsa = NULL;
                u8 *der;
                size_t derlen;
                struct sc_pkcs15_pubkey_info pubkey_info;
                struct sc_pkcs15_object     pubkey_obj;

                memset(&pubkey_info, 0, sizeof(pubkey_info));
                memset(&pubkey_obj,  0, sizeof(pubkey_obj));

                sc_pkcs15_format_id(pubkeys[i].id, &pubkey_info.id);
                pubkey_info.usage         = pubkeys[i].usage;
                pubkey_info.native        = 1;
                pubkey_info.key_reference = pubkeys[i].ref;
                pubkey_info.modulus_length= pubkeys[i].modulus_len;
                sc_format_path(pubkeys[i].path, &pubkey_info.path);
                /* subject and len? */

                strncpy(pubkey_obj.label, pubkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
                pubkey_obj.flags = pubkeys[i].obj_flags;
                if (pubkeys[i].auth_id)
                        sc_pkcs15_format_id(pubkeys[i].auth_id, &pubkey_obj.auth_id);

                r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
                if (r < 0)
                        SC_FUNC_RETURN(card->ctx, 1, r);
        }

        sc_debug(card->ctx, "PIV-II");

        /* set private keys */
        for (i = 0; prkeys[i].label; i++) {
                struct sc_pkcs15_prkey_info prkey_info;
                struct sc_pkcs15_object     prkey_obj;

                memset(&prkey_info, 0, sizeof(prkey_info));
                memset(&prkey_obj,  0, sizeof(prkey_obj));

                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;
                sc_format_path(prkeys[i].path, &prkey_info.path);

                strncpy(prkey_obj.label, prkeys[i].label, SC_PKCS15_MAX_LABEL_SIZE - 1);
                prkey_obj.flags = prkeys[i].obj_flags;
                if (prkeys[i].auth_id)
                        sc_pkcs15_format_id(prkeys[i].auth_id, &prkey_obj.auth_id);

                r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
                if (r < 0)
                        SC_FUNC_RETURN(card->ctx, 1, r);
        }
        SC_FUNC_RETURN(card->ctx, 1, SC_SUCCESS);
}

int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card,
                                  sc_pkcs15emu_opt_t *opts)
{
        sc_card_t   *card = p15card->card;
        sc_context_t    *ctx = card->ctx;

        SC_FUNC_CALLED(ctx, 1);

        if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
                return sc_pkcs15emu_piv_init(p15card);
        else {
                int r = piv_detect_card(p15card);
                if (r)
                        return SC_ERROR_WRONG_CARD;
                return sc_pkcs15emu_piv_init(p15card);
        }
}

/*
 * piv-tool.c: Tool for accessing smart cards with libopensc
 *
 * Copyright (C) 2001  Juha Yrjölä <[hidden email]>
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <string.h>
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>
#include <opensc/opensc.h>
#include <opensc/cardctl.h>
#include "util.h"
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/bn.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/err.h>

const char *app_name = "opensc-tool";

static int opt_reader = -1,
                opt_wait = 0;
static char ** opt_apdus;
static int opt_apdu_count = 0;
static int verbose = 0;

enum {
        OPT_SERIAL = 0x100,
};

const struct option options[] = {
        { "serial", 0, 0, OPT_SERIAL  },
        { "name", 0, 0, 'n' },
        { "admin", 0, 0, 'A' },
        { "genkey", 0, 0, 'G' },
        { "cert", 0, 0, 'C' },
        { "req", 0, 0, 'R' },
        { "out", 0, 0, 'o' },
        { "send-apdu", 1, 0, 's' },
        { "reader", 1, 0, 'r' },
        { "card-driver", 1, 0, 'c' },
        { "wait", 0, 0, 'w' },
        { "verbose", 0, 0, 'v' },
        { 0, 0, 0, 0 }
};

const char *option_help[] = {
        "Prints the card serial number",
        "Identify the card and print its name",
        "authenticate using default 3des key",
        "Generate a key on card, and output pubkey",
        "Load the AUTH cert onto card from file <arg>",
        "Generate a cert req",
        "Output file for cert or key or req",
        "Sends an APDU in format AA:BB:CC:DD:EE:FF...",
        "Uses reader number <arg> [0]",
        "Forces the use of driver <arg> [auto-detect]",
        "Wait for a card to be inserted",
        "Verbose operation. Use several times to enable debug output.",
};

sc_context_t *ctx = NULL;
sc_card_t *card = NULL;
BIO * bp = NULL;
RSA * newkey = NULL;

struct sc_cardctl_cryptoflex_genkey_info keydata = { 0x9a, 1024, 0, NULL, 0};

static int load_cert(char * cert_file)
{
        X509 * cert = NULL;
        FILE *fp;
        sc_path_t path;
        u8 *der = NULL;
        u8 *p;
        size_t derlen;
        int r;


    if((fp=fopen(cert_file, "r"))==NULL){
        printf("Cannot open cert file, %s %s\n",
                        cert_file, strerror(errno));
        return -1;
    }
        cert = PEM_read_X509(fp, &cert, NULL, NULL);
    fclose(fp);
    if(cert == NULL){
        printf("file %s does not conatin PEM-encoded certificate\n",
                         cert_file);
        return-1 ;
    }

        derlen = i2d_X509(cert, NULL);
        der = (u8 *) malloc(derlen);
        p = der;
        i2d_X509(cert, &p);
       
        sc_format_path("0101",&path);

        r = sc_select_file(card, &path, NULL);
        if (r < 0) {
                fprintf(stderr, "selectfile failed\n");
                 return -1;
        }
        r = sc_write_binary(card, 0, der, derlen, 0);
       
        return r;

}
static int admin_mode()
{
        int r;
        r = sc_card_ctl(card, SC_CARDCTL_LIFECYCLE_SET, NULL);
        if (r)
                fprintf(stderr, " admin_mode failed %d\n", r);
        return r;
}

/* generate a req using xxx as subject */
static int req()
{
        fprintf(stderr, "Not Implemented yet\n");
        return -1;
}

/* generate a new key pair, and save public key in newkey */
static int gen_key()
{
        int r;
        unsigned long expl;
        u8 expc[4];
       
//DEE need to get keyref, keytype, bits, etc, to pass in

        r = sc_card_ctl(card, SC_CARDCTL_CRYPTOFLEX_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);

        return r;

}

static int send_apdu(void)
{
        sc_apdu_t apdu;
        u8 buf[SC_MAX_APDU_BUFFER_SIZE], sbuf[SC_MAX_APDU_BUFFER_SIZE],
           rbuf[SC_MAX_APDU_BUFFER_SIZE], *p;
        size_t len, len0, r;
        int c;

        for (c = 0; c < opt_apdu_count; c++) {
                len0 = sizeof(buf);
                sc_hex_to_bin(opt_apdus[c], buf, &len0);
                if (len0 < 4) {
                        fprintf(stderr, "APDU too short (must be at least 4 bytes).\n");
                        return 2;
                }
                len = len0;
                p = buf;
                memset(&apdu, 0, sizeof(apdu));
                apdu.cla = *p++;
                apdu.ins = *p++;
                apdu.p1 = *p++;
                apdu.p2 = *p++;
                apdu.resp = rbuf;
                apdu.resplen = sizeof(rbuf);
                len -= 4;
                if (len > 1) {
                        apdu.lc = *p++;
                        len--;
                        memcpy(sbuf, p, apdu.lc);
                        apdu.data = sbuf;
                        apdu.datalen = apdu.lc;
                        if (len < apdu.lc) {
                                fprintf(stderr, "APDU too short (need %d bytes).\n",
                                        apdu.lc-len);
                                return 2;
                        }
                        len -= apdu.lc;
                        if (len) {
                                apdu.le = *p++;
                                if (apdu.le == 0)
                                        apdu.le = 256;
                                len--;
                                apdu.cse = SC_APDU_CASE_4_SHORT;
                        } else
                                apdu.cse = SC_APDU_CASE_3_SHORT;
                        if (len) {
                                fprintf(stderr, "APDU too long (%d bytes extra).\n", len);
                                return 2;
                        }
                } else if (len == 1) {
                        apdu.le = *p++;
                        if (apdu.le == 0)
                                apdu.le = 256;
                        len--;
                        apdu.cse = SC_APDU_CASE_2_SHORT;
                } else
                        apdu.cse = SC_APDU_CASE_1;
                printf("Sending: ");
                for (r = 0; r < len0; r++)
                        printf("%02X ", buf[r]);
                printf("\n");
                r = sc_transmit_apdu(card, &apdu);
                if (r) {
                        fprintf(stderr, "APDU transmit failed: %s\n", sc_strerror(r));
                        return 1;
                }
                printf("Received (SW1=0x%02X, SW2=0x%02X)%s\n", apdu.sw1, apdu.sw2,
                       apdu.resplen ? ":" : "");
                if (apdu.resplen)
                        hex_dump_asc(stdout, apdu.resp, apdu.resplen, -1);
        }
        return 0;
}

static void print_serial(sc_card_t *in_card)
{
        int r;
        sc_serial_number_t serial;

        r = sc_card_ctl(in_card, SC_CARDCTL_GET_SERIALNR, &serial);
        if (r)
                fprintf(stderr, "sc_card_ctl(*, SC_CARDCTL_GET_SERIALNR, *) failed\n");
        else
                hex_dump_asc(stdout, serial.value, serial.len, -1);
}

int main(int argc, char * const argv[])
{
        int err = 0, r, c, long_optind = 0;
        int do_send_apdu = 0;
        int do_admin_mode = 0;
        int do_gen_key = 0;
        int do_load_cert = 0;
        int do_req = 0;
        int do_print_serial = 0;
        int do_print_name = 0;
        int action_count = 0;
        const char *opt_driver = NULL;
        const char *out_file = NULL;
        const char *cert_file = NULL;
               
        setbuf(stderr, NULL);
        setbuf(stdout, NULL);

        while (1) {
                c = getopt_long(argc, argv, "nAGC:Ro:fvs:c:w", options, &long_optind);
                if (c == -1)
                        break;
                if (c == '?')
                        print_usage_and_die();
                switch (c) {
                case 's':
                        opt_apdus = (char **) realloc(opt_apdus,
                                        (opt_apdu_count + 1) * sizeof(char *));
                        opt_apdus[opt_apdu_count] = optarg;
                        do_send_apdu++;
                        if (opt_apdu_count == 0)
                                action_count++;
                        opt_apdu_count++;
                        break;
                case 'n':
                        do_print_name = 1;
                        action_count++;
                        break;
                case 'A':
                        do_admin_mode = 1;
                        action_count++;
                        break;
                case 'G':
                        do_gen_key = 1;
                        action_count++;
                        break;
                case 'C':
                        do_load_cert = 1;
                        cert_file = optarg;
                        action_count++;
                        break;
                case 'R':
                        do_req = 1;
                        action_count++;
                        break;
                case 'o':
                        out_file = optarg;
                        break;
                case 'r':
                        opt_reader = atoi(optarg);
                        break;
                case 'v':
                        verbose++;
                        break;
                case 'c':
                        opt_driver = optarg;
                        break;
                case 'w':
                        opt_wait = 1;
                        break;
                }
        }
        if (action_count == 0)
                print_usage_and_die();

        CRYPTO_malloc_init();
        ERR_load_crypto_strings();
        OpenSSL_add_all_algorithms();


        if (out_file) {
                bp = BIO_new(BIO_s_file());
                BIO_write_filename(bp, out_file);
        } else {
                bp = BIO_new(BIO_s_file());
                BIO_set_fp(bp,stdout,BIO_NOCLOSE);
        }

        r = sc_establish_context(&ctx, app_name);
        if (r) {
                fprintf(stderr, "Failed to establish context: %s\n", sc_strerror(r));
                return 1;
        }
        if (verbose > 1)
                ctx->debug = verbose-1;

        if (action_count <= 0)
                goto end;

        if (opt_driver != NULL) {
                err = sc_set_card_driver(ctx, opt_driver);
                if (err) {
                        fprintf(stderr, "Driver '%s' not found!\n", opt_driver);
                        err = 1;
                        goto end;
                }
        }

        err = connect_card(ctx, &card, opt_reader, 0, opt_wait, verbose);
        if (err)
                goto end;

        if (do_admin_mode) {
                if ((err = admin_mode()))
                        goto end;
                action_count--;
        }
        if (do_gen_key) {
                if ((err = gen_key()))
                        goto end;
                action_count--;
        }
        if (do_load_cert) {
                if ((err = load_cert(cert_file)))
                        goto end;
                action_count--;
        }
        if (do_print_serial) {
                if (verbose)
                        printf("Card serial number:");
                print_serial(card);
                action_count--;
        }
        if (do_print_name) {
                if (verbose)
                        printf("Card name: ");
                printf("%s\n", card->name);
                action_count--;
        }
        if (do_send_apdu) {
                if ((err = send_apdu()))
                        goto end;
                action_count--;
        }
       
end:
        if (bp)
                BIO_free(bp);
        if (card) {
                sc_unlock(card);
                sc_disconnect_card(card, 0);
        }
        if (ctx)
                sc_release_context(ctx);
        return err;
}

_______________________________________________
opensc-devel mailing list
[hidden email]
http://www.opensc.org/cgi-bin/mailman/listinfo/opensc-devel