diff --git a/libcaccard/cac.c b/libcaccard/cac.c
new file mode 100644
index 0000000..e51caec
--- /dev/null
+++ b/libcaccard/cac.c
@@ -0,0 +1,411 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#include "cac.h"
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.h>
+
+#define CAC_GET_PROPERTIES  0x56
+#define CAC_GET_ACR         0x4c
+#define CAC_READ_BUFFER     0x52
+#define CAC_UPDATE_BUFFER   0x58
+#define CAC_SIGN_DECRYPT    0x42
+#define CAC_GET_CERTIFICATE 0x36
+
+/* private data for PKI applets */
+typedef struct CACPKIAppletDataStruct {
+    unsigned char *cert;
+    int cert_len;
+    unsigned char *cert_buffer;
+    int cert_buffer_len;
+    unsigned char *sign_buffer;
+    int sign_buffer_len;
+    VCardKey *key;
+} CACPKIAppletData;
+
+/*
+ * CAC applet private data
+ */
+struct VCardAppletPrivateStruct {
+    union {
+        CACPKIAppletData pki_data;
+        void *reserved;
+    } u;
+};
+
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+cac_common_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    int ef;
+
+    switch (apdu->a_ins) {
+    case VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x02) {
+            /* let the 7816 code handle applet switches */
+            return VCARD_NEXT;
+        }
+        /* handle file id setting */
+        if (apdu->a_Lc != 2) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_DATA_INVALID);
+            return VCARD_DONE;
+        }
+        /* CAC 1.0 only supports ef = 0 */
+        ef = apdu->a_body[0] | (apdu->a_body[1] << 8);
+        if (ef != 0 ) {
+            *response = vcard_make_response(
+                VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+            return VCARD_DONE;
+        }
+        *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+        return VCARD_DONE;
+    case VCARD7816_INS_GET_RESPONSE:
+    case VCARD7816_INS_VERIFY:
+        /* let the 7816 code handle these */
+        return VCARD_NEXT;
+    case CAC_GET_PROPERTIES:
+    case CAC_GET_ACR:
+        /* skip these for now, this will probably be needed */
+        *response = vcard_make_response(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+        return VCARD_DONE;
+    }
+    *response = vcard_make_response(
+        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ *  resest the inter call state between applet selects
+ */
+static VCardStatus
+cac_applet_pki_reset(VCard *card, int channel)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    CACPKIAppletData *pki_applet = NULL;
+    applet_private = vcard_get_current_applet_private(card, channel);
+    ASSERT(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    pki_applet->cert_buffer = NULL;
+    if (pki_applet->sign_buffer) {
+        free(pki_applet->sign_buffer);
+        pki_applet->sign_buffer = NULL;
+    }
+    pki_applet->cert_buffer_len = 0;
+    pki_applet->sign_buffer_len = 0;
+    return VCARD_DONE;
+}
+
+static VCardStatus
+cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu,
+                            VCardResponse **response)
+{
+    CACPKIAppletData *pki_applet = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    int size, next;
+    unsigned char *sign_buffer;
+    vcard_7816_status_t status;
+
+    applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
+    ASSERT(applet_private);
+    pki_applet = &(applet_private->u.pki_data);
+
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+            VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_GET_CERTIFICATE:
+        if ((apdu->a_p2 != 0) || (apdu->a_p1 != 0)) {
+           *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+           break;
+        }
+        ASSERT(pki_applet->cert != NULL);
+        size = apdu->a_Le;
+        if (pki_applet->cert_buffer == NULL) {
+            pki_applet->cert_buffer=pki_applet->cert;
+            pki_applet->cert_buffer_len=pki_applet->cert_len;
+        }
+        size = MIN(size, pki_applet->cert_buffer_len);
+        next = MIN(255, pki_applet->cert_buffer_len - size);
+        *response = vcard_response_new_bytes(
+                        card, pki_applet->cert_buffer, size,
+                        apdu->a_Le, next ?
+                        VCARD7816_SW1_WARNING_CHANGE :
+                        VCARD7816_SW1_SUCCESS,
+                        next);
+        pki_applet->cert_buffer += size;
+        pki_applet->cert_buffer_len -= size;
+        if ((*response == NULL) || (next == 0)) {
+            pki_applet->cert_buffer=NULL;
+        }
+        if (*response == NULL) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        return VCARD_DONE;
+    case CAC_SIGN_DECRYPT:
+        if (apdu->a_p2 != 0) {
+           *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+           break;
+        }
+        size = apdu->a_Lc;
+
+        sign_buffer = realloc(pki_applet->sign_buffer,
+                      pki_applet->sign_buffer_len+size);
+        if (sign_buffer == NULL) {
+            free(pki_applet->sign_buffer);
+            pki_applet->sign_buffer = NULL;
+            pki_applet->sign_buffer_len = 0;
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            return VCARD_DONE;
+        }
+        memcpy(sign_buffer+pki_applet->sign_buffer_len, apdu->a_body, size);
+        size += pki_applet->sign_buffer_len;
+        switch (apdu->a_p1) {
+        case  0x80:
+            /* p1 == 0x80 means we haven't yet sent the whole buffer, wait for
+             * the rest */
+            pki_applet->sign_buffer = sign_buffer;
+            pki_applet->sign_buffer_len = size;
+            *response = vcard_make_response(VCARD7816_STATUS_SUCCESS);
+            return VCARD_DONE;
+        case 0x00:
+            /* we now have the whole buffer, do the operation, result will be
+             * in the sign_buffer */
+            status = vcard_emul_rsa_op(card, pki_applet->key,
+                                       sign_buffer, size);
+            if (status != VCARD7816_STATUS_SUCCESS) {
+                *response = vcard_make_response(status);
+                break;
+            }
+            *response = vcard_response_new(card, sign_buffer, size, apdu->a_Le,
+                                                     VCARD7816_STATUS_SUCCESS);
+            if (*response == NULL) {
+                *response = vcard_make_response(
+                                VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+            }
+            break;
+        default:
+           *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+            break;
+        }
+        free(sign_buffer);
+        pki_applet->sign_buffer = NULL;
+        pki_applet->sign_buffer_len = 0;
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                                VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+static VCardStatus
+cac_applet_id_process_apdu(VCard *card, VCardAPDU *apdu,
+                           VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+        return VCARD_DONE;
+    case CAC_READ_BUFFER:
+        /* new CAC call, go ahead and use the old version for now */
+        /* TODO: implement */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+
+/*
+ * TODO: if we ever want to support general CAC middleware, we will need to
+ * implement the various containers.
+ */
+static VCardStatus
+cac_applet_container_process_apdu(VCard *card, VCardAPDU *apdu,
+                                  VCardResponse **response)
+{
+    switch (apdu->a_ins) {
+    case CAC_READ_BUFFER:
+    case CAC_UPDATE_BUFFER:
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    default:
+        break;
+    }
+    return cac_common_process_apdu(card, apdu, response);
+}
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+cac_delete_pki_applet_private(VCardAppletPrivate *applet_private)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    if (pki_applet_data == NULL) {
+        return;
+    }
+    pki_applet_data = &(applet_private->u.pki_data);
+    if (pki_applet_data->cert != NULL) {
+        free(pki_applet_data->cert);
+    }
+    if (pki_applet_data->sign_buffer != NULL) {
+        free(pki_applet_data->sign_buffer);
+    }
+    if (pki_applet_data->key != NULL) {
+        vcard_emul_delete_key(pki_applet_data->key);
+    }
+    free(applet_private);
+}
+
+static VCardAppletPrivate *
+cac_new_pki_applet_private(const unsigned char *cert,
+                           int cert_len, VCardKey *key)
+{
+    CACPKIAppletData *pki_applet_data = NULL;
+    VCardAppletPrivate *applet_private = NULL;
+    applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+    if (applet_private == NULL) {
+        goto fail;
+    }
+    pki_applet_data= &(applet_private->u.pki_data);
+    pki_applet_data->cert_buffer = NULL;
+    pki_applet_data->cert_buffer_len = 0;
+    pki_applet_data->sign_buffer = NULL;
+    pki_applet_data->sign_buffer_len = 0;
+    pki_applet_data->key = NULL;
+    pki_applet_data->cert = (unsigned char *)malloc(cert_len+1);
+    if (pki_applet_data->cert == NULL) {
+        goto fail;
+    }
+    /*
+     * if we want to support compression, then we simply change the 0 to a 1
+     * and compress the cert data with libz
+     */
+    pki_applet_data->cert[0] = 0; /* not compressed */
+    memcpy(&pki_applet_data->cert[1], cert, cert_len);
+    pki_applet_data->cert_len = cert_len+1;
+
+    pki_applet_data->key = key;
+    return applet_private;
+
+fail:
+    if (applet_private) {
+        cac_delete_pki_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+/*
+ * create a new cac applet which links to a given cert
+ */
+static VCardApplet *
+cac_new_pki_applet(int i, const unsigned char *cert,
+                   int cert_len, VCardKey *key)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    VCardApplet *applet = NULL;
+    unsigned char pki_aid[] = { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x01, 0x00 };
+    int pki_aid_len = sizeof (pki_aid);
+
+    pki_aid[pki_aid_len-1] = i;
+
+    applet_private = cac_new_pki_applet_private(cert, cert_len, key);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+    applet = vcard_new_applet(cac_applet_pki_process_apdu, cac_applet_pki_reset,
+                              pki_aid, pki_aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_set_applet_private(applet, applet_private,
+                             cac_delete_pki_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        cac_delete_pki_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+static unsigned char cac_default_container_aid[] =
+    { 0xa0, 0x00, 0x00, 0x00, 0x30, 0x00, 0x00 };
+static unsigned char cac_id_aid[] =
+    { 0xa0, 0x00, 0x00, 0x00, 0x79, 0x03, 0x00 };
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+cac_card_init(VReader *reader, VCard *card,
+              const char *params,
+              unsigned char * const *cert,
+              int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count)
+{
+    int i;
+    VCardApplet *applet;
+
+    /* CAC Cards are VM Cards */
+    vcard_set_type(card,VCARD_VM);
+
+    /* create one PKI applet for each cert */
+    for (i=0; i < cert_count; i++) {
+        applet = cac_new_pki_applet(i, cert[i], cert_len[i], key[i]);
+        if (applet == NULL) {
+            goto failure;
+        }
+        vcard_add_applet(card, applet);
+    }
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_container_process_apdu,
+                              NULL, cac_default_container_aid,
+                              sizeof(cac_default_container_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+
+    /* create a default blank container applet */
+    applet = vcard_new_applet(cac_applet_id_process_apdu,
+                              NULL, cac_id_aid,
+                              sizeof(cac_id_aid));
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_add_applet(card, applet);
+    return VCARD_DONE;
+
+failure:
+    return VCARD_FAIL;
+}
+
diff --git a/libcaccard/cac.h b/libcaccard/cac.h
new file mode 100644
index 0000000..bb2a9f0
--- /dev/null
+++ b/libcaccard/cac.h
@@ -0,0 +1,20 @@
+/*
+ * defines the entry point for the cac card. Only used by cac.c anc
+ * vcard_emul_type.c
+ */
+#ifndef CAC_H
+#define CAC_H 1
+#include "vcard.h"
+#include "vreader.h"
+/*
+ * Initialize the cac card. This is the only public function in this file. All
+ * the rest are connected through function pointers.
+ */
+VCardStatus cac_card_init(VReader *reader, VCard *card, const char *params,
+              unsigned char * const *cert, int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count);
+
+/* not yet implemented */
+VCardStatus cac_is_cac_card(VReader *reader);
+#endif
diff --git a/libcaccard/card_7816.c b/libcaccard/card_7816.c
new file mode 100644
index 0000000..e2d8466
--- /dev/null
+++ b/libcaccard/card_7816.c
@@ -0,0 +1,780 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * set the status bytes based on the status word
+ */
+static void
+vcard_response_set_status(VCardResponse *response, vcard_7816_status_t status)
+{
+    unsigned char sw1, sw2;
+    response->b_status = status; /* make sure the status and swX representations
+                                  * are consistent */
+    sw1 = (status >> 8) & 0xff;
+    sw2 = status & 0xff;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * set the status bytes in a response buffer
+ */
+static void
+vcard_response_set_status_bytes(VCardResponse *response,
+                               unsigned char sw1, unsigned char sw2)
+{
+    response->b_status = sw1 << 8 | sw2;
+    response->b_sw1 = sw1;
+    response->b_sw2 = sw2;
+    response->b_data[response->b_len] = sw1;
+    response->b_data[response->b_len+1] = sw2;
+}
+
+/*
+ * allocate a VCardResponse structure, plus space for the data buffer, and
+ * set up everything but the resonse bytes.
+ */
+VCardResponse *
+vcard_response_new_data(unsigned char *buf, int len)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = malloc(len+2);
+    if (!new_response->b_data) {
+        free(new_response);
+        return NULL;
+    }
+    memcpy(new_response->b_data, buf, len);
+    new_response->b_total_len = len+2;
+    new_response->b_len = len;
+    new_response->b_type = VCARD_MALLOC;
+    return new_response;
+}
+
+static VCardResponse *
+vcard_init_buffer_response(VCard *card, unsigned char *buf, int len)
+{
+    VCardResponse *response;
+    VCardBufferResponse *buffer_response;
+
+    buffer_response =vcard_get_buffer_response(card);
+    if (buffer_response) {
+        vcard_set_buffer_response(card, NULL);
+        vcard_buffer_response_delete(buffer_response);
+    }
+    buffer_response = vcard_buffer_response_new(buf, len);
+    if (buffer_response == NULL) {
+        return NULL;
+    }
+    response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
+                                               len > 255 ? 0 : len);
+    if (response == NULL) {
+        return NULL;
+    }
+    vcard_set_buffer_response(card,buffer_response);
+    return response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new(VCard *card, unsigned char *buf,
+                   int len, int Le, vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+       return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf,len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status(new_response,status);
+    return new_response;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new_bytes(VCard *card, unsigned char *buf, int len, int Le,
+                         unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    if (len > Le) {
+       return vcard_init_buffer_response(card, buf, len);
+    }
+    new_response = vcard_response_new_data(buf,len);
+    if (new_response == NULL) {
+        return NULL;
+    }
+    vcard_response_set_status_bytes(new_response,sw1,sw2);
+    return new_response;
+}
+
+/*
+ * get a new Reponse buffer that only has a status.
+ */
+static VCardResponse *
+vcard_response_new_status(vcard_7816_status_t status)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status(new_response,status);
+    return new_response;
+}
+
+/*
+ * same as above, but specify the status as separate bytes
+ */
+VCardResponse *
+vcard_response_new_status_bytes(unsigned char sw1, unsigned char sw2)
+{
+    VCardResponse *new_response;
+
+    new_response = (VCardResponse *)malloc(sizeof(VCardResponse));
+    if (!new_response) {
+        return NULL;
+    }
+    new_response->b_data = &new_response->b_sw1;
+    new_response->b_len = 0;
+    new_response->b_total_len = 2;
+    new_response->b_type = VCARD_MALLOC_STRUCT;
+    vcard_response_set_status_bytes(new_response, sw1, sw2);
+    return new_response;
+}
+
+
+/*
+ * free the response buffer. The Buffer has a type to handle the buffer
+ * allocated in other ways than through malloc.
+ */
+void
+vcard_response_delete(VCardResponse *response)
+{
+    if (response == NULL) {
+        return;
+    }
+    switch (response->b_type) {
+    case VCARD_MALLOC:
+        /* everything was malloc'ed */
+        if (response->b_data) {
+            free(response->b_data);
+        }
+        free(response);
+        break;
+    case VCARD_MALLOC_DATA:
+        /* only the data buffer was malloc'ed */
+        if (response->b_data) {
+            free(response->b_data);
+        }
+        break;
+     case VCARD_MALLOC_STRUCT:
+        /* only the structure was malloc'ed */
+        free(response);
+        break;
+     case VCARD_STATIC:
+        break;
+     }
+}
+
+/*
+ * decode the class bit and set our generic type field, channel, and
+ * secure messaging values.
+ */
+static vcard_7816_status_t
+vcard_apdu_set_class(VCardAPDU *apdu) {
+    apdu->a_channel = 0;
+    apdu->a_secure_messaging = 0;
+    apdu->a_type = apdu->a_cla & 0xf0;
+    apdu->a_gen_type = VCARD_7816_ISO;
+
+    /* parse the class  tables 8 & 9 of the 7816-4 Part 4 spec */
+    switch (apdu->a_type) {
+        /* we only support the basic types */
+    case 0x00:
+    case 0x80:
+    case 0x90:
+    case 0xa0:
+        apdu->a_channel = apdu->a_cla & 3;
+        apdu->a_secure_messaging = apdu->a_cla & 0xe;
+        break;
+    case 0xb0:
+    case 0xc0:
+        break;
+
+    case 0x10:
+    case 0x20:
+    case 0x30:
+    case 0x40:
+    case 0x50:
+    case 0x60:
+    case 0x70:
+        /* Reserved for future use */
+        apdu->a_gen_type = VCARD_7816_RFU;
+        break;
+    case 0xd0:
+    case 0xe0:
+    case 0xf0:
+    default:
+        apdu->a_gen_type =
+            (apdu->a_cla == 0xff)? VCARD_7816_PTS : VCARD_7816_PROPIETARY;
+        break;
+    }
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * set the Le and Lc fiels according to table 5 of the
+ * 7816-4 part 4 spec
+ */
+static vcard_7816_status_t
+vcard_apdu_set_length(VCardAPDU *apdu)
+{
+    int L, Le;
+
+    /* process according to table 5 of the 7816-4 Part 4 spec.
+     * variable names match the variables in the spec */
+    L = apdu->a_len-4; /* fixed APDU header */
+    apdu->a_Lc = 0;
+    apdu->a_Le = 0;
+    apdu->a_body = NULL;
+    switch (L) {
+    case 0:
+        /* 1 minimal apdu */
+        return VCARD7816_STATUS_SUCCESS;
+    case 1:
+        /* 2S only return values apdu */
+        /*   zero maps to 256 here */
+        apdu->a_Le = apdu->a_header->ah_Le ?
+                         apdu->a_header->ah_Le : 256;
+        return VCARD7816_STATUS_SUCCESS;
+    default:
+        /* if the ah_Le byte is zero and we have more than
+         * 1 byte in the header, then we must be using extended Le and Lc.
+         * process the extended now. */
+        if (apdu->a_header->ah_Le == 0) {
+            if (L < 3) {
+                /* coding error, need at least 3 bytes */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* calculate the first extended value. Could be either Le or Lc */
+            Le = (apdu->a_header->ah_body[0] << 8)
+               || apdu->a_header->ah_body[1];
+            if (L == 3) {
+                /* 2E extended, return data only */
+                /*   zero maps to 65536 */
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (Le == 0) {
+                /* reserved for future use, probably for next time we need
+                 * to extend the lengths */
+                return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+            }
+            /* we know that the first extended value is Lc now */
+            apdu->a_Lc = Le;
+            apdu->a_body = &apdu->a_header->ah_body[2];
+            if (L == Le+3) {
+                /* 3E extended, only body parameters */
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            if (L == Le+5) {
+                /* 4E extended, parameters and return data */
+                Le = (apdu->a_data[apdu->a_len-2] << 8)
+                   || apdu->a_data[apdu->a_len-1];
+                apdu->a_Le = Le ? Le : 65536;
+                return VCARD7816_STATUS_SUCCESS;
+            }
+            return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        }
+        /* not extended */
+        apdu->a_Lc= apdu->a_header->ah_Le;
+        apdu->a_body = &apdu->a_header->ah_body[0];
+        if (L ==  apdu->a_Lc + 1) {
+            /* 3S only body parameters */
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        if (L ==  apdu->a_Lc + 2) {
+            /* 4S parameters and return data */
+            Le = apdu->a_data[apdu->a_len-1];
+            apdu->a_Le = Le ?  Le : 256;
+            return VCARD7816_STATUS_SUCCESS;
+        }
+        break;
+    }
+    return VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+}
+
+/*
+ * create a new APDU from a raw set of bytes. This will decode all the
+ * above fields. users of VCARDAPDU's can then depend on the already decoded
+ * values.
+ */
+VCardAPDU *
+vcard_apdu_new(unsigned char *raw_apdu, int len, vcard_7816_status_t *status)
+{
+    VCardAPDU *new_apdu;
+
+    *status = VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    if (len < 4) {
+        *status = VCARD7816_STATUS_ERROR_WRONG_LENGTH;
+        return NULL;
+    }
+
+    new_apdu = (VCardAPDU *)malloc(sizeof(VCardAPDU));
+    if (!new_apdu) {
+        return NULL;
+    }
+    new_apdu->a_data = malloc(len);
+    if (!new_apdu->a_data) {
+        free(new_apdu);
+        return NULL;
+    }
+    memcpy(new_apdu->a_data, raw_apdu, len);
+    new_apdu->a_len = len;
+    *status = vcard_apdu_set_class(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        free(new_apdu);
+        return NULL;
+    }
+    *status = vcard_apdu_set_length(new_apdu);
+    if (*status != VCARD7816_STATUS_SUCCESS) {
+        free(new_apdu);
+        new_apdu = NULL;
+    }
+    return new_apdu;
+}
+
+void
+vcard_apdu_delete(VCardAPDU *apdu)
+{
+    if (apdu == NULL) {
+        return;
+    }
+    if (apdu->a_data) {
+        free(apdu->a_data);
+    }
+    free(apdu);
+}
+
+
+/*
+ * declare response buffers for all the 7816 defined error codes
+ */
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_SUCCESS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_RET_CORUPT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_WARNING_FILE_FILLED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_CHANGE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_LENGTH)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NO_EF)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS)
+VCARD_RESPONSE_NEW_STATIC_STATUS(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_FILE_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_P1_P2_INCORRECT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_DATA_NOT_FOUND)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_INS_CODE_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_CLA_INVALID)
+VCARD_RESPONSE_NEW_STATIC_STATUS(VCARD7816_STATUS_ERROR_GENERAL)
+
+/*
+ * return a single response code. This function cannot fail. It will always
+ * return a response.
+ */
+VCardResponse *
+vcard_make_response(vcard_7816_status_t status)
+{
+    VCardResponse *response = NULL;
+
+    switch (status) {
+    /* known 7816 response codes */
+    case VCARD7816_STATUS_SUCCESS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_SUCCESS);
+    case VCARD7816_STATUS_WARNING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING);
+    case VCARD7816_STATUS_WARNING_RET_CORUPT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_RET_CORUPT);
+    case VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE);
+    case VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED);
+    case VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID);
+    case VCARD7816_STATUS_WARNING_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_CHANGE);
+    case VCARD7816_STATUS_WARNING_FILE_FILLED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_WARNING_FILE_FILLED);
+    case VCARD7816_STATUS_EXC_ERROR:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR);
+    case VCARD7816_STATUS_EXC_ERROR_CHANGE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_CHANGE);
+    case VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+    case VCARD7816_STATUS_ERROR_WRONG_LENGTH:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_LENGTH);
+    case VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE);
+    case VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED);
+    case VCARD7816_STATUS_ERROR_DATA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_INVALID);
+    case VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED);
+    case VCARD7816_STATUS_ERROR_DATA_NO_EF:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NO_EF);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING);
+    case VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA);
+    case VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+    case VCARD7816_STATUS_ERROR_FILE_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE);
+    case VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_P1_P2_INCORRECT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_P1_P2_INCORRECT);
+    case VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT);
+    case VCARD7816_STATUS_ERROR_DATA_NOT_FOUND:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+    case VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2);
+    case VCARD7816_STATUS_ERROR_INS_CODE_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_INS_CODE_INVALID);
+    case VCARD7816_STATUS_ERROR_CLA_INVALID:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_CLA_INVALID);
+    case VCARD7816_STATUS_ERROR_GENERAL:
+        return VCARD_RESPONSE_GET_STATIC(
+                    VCARD7816_STATUS_ERROR_GENERAL);
+    default:
+        /* we don't know this status code, create a response buffer to
+         * hold it */
+        response = vcard_response_new_status(status);
+        if (response == NULL) {
+            /* couldn't allocate the buffer, return memmory error */
+            return VCARD_RESPONSE_GET_STATIC(
+                        VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+    }
+    ASSERT(response);
+    return response;
+}
+
+/*
+ * Add File card support here if you need it.
+ */
+static VCardStatus
+vcard7816_file_system_process_apdu(VCard *card, VCardAPDU *apdu,
+                                   VCardResponse **response)
+{
+    /* TODO: if we want to support a virtual file system card, we do it here.
+     * It would probably be a pkcs #15 card type */
+    *response = vcard_make_response(
+                    VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
+
+/*
+ * VM card (including java cards)
+ */
+static VCardStatus
+vcard7816_vm_process_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    int bytes_to_copy, next_byte_count, count;
+    VCardApplet *current_applet;
+    VCardBufferResponse *buffer_response;
+    vcard_7816_status_t status;
+
+    /* parse the class first */
+    if (apdu->a_gen_type !=  VCARD_7816_ISO) {
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* use a switch so that if we need to support secure channel stuff later,
+     * we know where to put it */
+    switch (apdu->a_secure_messaging) {
+    case 0x0: /* no SM */
+        break;
+    case 0x4: /* proprietary SM */
+    case 0x8: /* header not authenticated */
+    case 0xc: /* header authenticated */
+    default:
+        /* for now, don't try to support secure channel stuff in the
+         * virtual card. */
+        *response = vcard_make_response(
+                        VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED);
+        return VCARD_DONE;
+    }
+
+    /* now parse the instruction */
+    switch (apdu->a_ins) {
+    case  VCARD7816_INS_MANAGE_CHANNEL: /* secure channel op */
+    case  VCARD7816_INS_EXTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_GET_CHALLENGE: /* secure channel op */
+    case  VCARD7816_INS_INTERNAL_AUTHENTICATE: /* secure channel op */
+    case  VCARD7816_INS_ERASE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_BINARY: /* applet control op */
+    case  VCARD7816_INS_WRITE_BINARY: /* applet control op */
+    case  VCARD7816_INS_UPDATE_BINARY: /* applet control op */
+    case  VCARD7816_INS_READ_RECORD: /* file op */
+    case  VCARD7816_INS_WRITE_RECORD: /* file op */
+    case  VCARD7816_INS_UPDATE_RECORD: /* file op */
+    case  VCARD7816_INS_APPEND_RECORD: /* file op */
+    case  VCARD7816_INS_ENVELOPE:
+    case  VCARD7816_INS_PUT_DATA:
+        *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    case  VCARD7816_INS_SELECT_FILE:
+        if (apdu->a_p1 != 0x04) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED);
+            break;
+        }
+
+        /* side effect, deselect the current applet if no applet has been found
+         * */
+        current_applet = vcard_find_applet(card, apdu->a_body, apdu->a_Lc);
+        vcard_select_applet(card, apdu->a_channel, current_applet);
+        if (current_applet) {
+            unsigned char *aid;
+            int aid_len;
+            aid = vcard_applet_get_aid(current_applet, &aid_len);
+           *response = vcard_response_new(card, aid, aid_len, apdu->a_Le,
+                                          VCARD7816_STATUS_SUCCESS);
+        } else {
+           *response = vcard_make_response(
+                             VCARD7816_STATUS_ERROR_FILE_NOT_FOUND);
+        }
+        break;
+
+    case  VCARD7816_INS_VERIFY:
+        if ((apdu->a_p1 != 0x00) || (apdu->a_p2 != 0x00)) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_WRONG_PARAMETERS);
+        } else {
+            if (apdu->a_Lc == 0) {
+                /* handle pin count if possible */
+                count = vcard_emul_get_login_count(card);
+                if (count < 0) {
+                    *response = vcard_make_response(
+                                    VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+                } else {
+                    if (count > 0xf) {
+                        count = 0xf;
+                    }
+                    *response = vcard_response_new_status_bytes(
+                                                VCARD7816_SW1_WARNING_CHANGE,
+                                                                0xc0 | count);
+                    if (*response == NULL) {
+                        *response = vcard_make_response(
+                                    VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+                    }
+                }
+            } else {
+                    status = vcard_emul_login(card, apdu->a_body, apdu->a_Lc);
+                *response = vcard_make_response(status);
+            }
+        }
+        break;
+
+    case VCARD7816_INS_GET_RESPONSE:
+        buffer_response = vcard_get_buffer_response(card);
+        if (!buffer_response) {
+            *response = vcard_make_response(
+                            VCARD7816_STATUS_ERROR_DATA_NOT_FOUND);
+            /* handle error */
+        }
+        bytes_to_copy = MIN(buffer_response->len, apdu->a_Le);
+        next_byte_count = MIN(256, buffer_response->len - bytes_to_copy);
+        *response = vcard_response_new_bytes(
+                        card, buffer_response->current, bytes_to_copy,
+                        apdu->a_Le,
+                        next_byte_count ?
+                        VCARD7816_SW1_RESPONSE_BYTES: VCARD7816_SW1_SUCCESS,
+                        next_byte_count);
+        buffer_response->current += bytes_to_copy;
+        buffer_response->len -= bytes_to_copy;
+        if (*response == NULL || (next_byte_count == 0)) {
+            vcard_set_buffer_response(card,NULL);
+            vcard_buffer_response_delete(buffer_response);
+        }
+        if (*response == NULL) {
+            *response =
+                vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+        }
+        break;
+
+    case VCARD7816_INS_GET_DATA:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+
+    default:
+        *response =
+            vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+        break;
+    }
+
+    /* response should have been set somewhere */
+    ASSERT(*response != NULL);
+    return VCARD_DONE;
+}
+
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location.
+ */
+VCardStatus
+vcard_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    VCardStatus status;
+    VCardBufferResponse *buffer_response;
+
+    /* first handle any PTS commands, which aren't really APDU's */
+    if (apdu->a_type == VCARD_7816_PTS) {
+        /* the PTS responses aren't really responses either */
+        *response = vcard_response_new_data(apdu->a_data, apdu->a_len);
+        /* PTS responses have no status bytes */
+        (*response)->b_total_len = (*response)->b_len;
+        return VCARD_DONE;
+    }
+    buffer_response = vcard_get_buffer_response(card);
+    if (buffer_response && apdu->a_ins != VCARD7816_INS_GET_RESPONSE) {
+       /* clear out buffer_response, return an error */
+        vcard_set_buffer_response(card,NULL);
+        vcard_buffer_response_delete(buffer_response);
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    status = vcard_process_applet_apdu(card, apdu, response);
+    if (status != VCARD_NEXT) {
+        return status;
+    }
+    switch (vcard_get_type(card)) {
+    case VCARD_FILE_SYSTEM:
+        return vcard7816_file_system_process_apdu(card,apdu,response);
+    case VCARD_VM:
+        return vcard7816_vm_process_apdu(card,apdu,response);
+    case VCARD_DIRECT:
+        /* if we are type direct, then the applet should handle everything */
+        assert("VCARD_DIRECT: applet failure");
+       break;
+    }
+    *response =
+        vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+    return VCARD_DONE;
+}
diff --git a/libcaccard/card_7816.h b/libcaccard/card_7816.h
new file mode 100644
index 0000000..4351b1c
--- /dev/null
+++ b/libcaccard/card_7816.h
@@ -0,0 +1,60 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#ifndef CARD_7816_H
+#define CARD_7816_H  1
+
+#include "card_7816t.h"
+#include "vcardt.h"
+
+/*
+ * constructors for VCardResponse's
+ */
+/* response from a return buffer and a status */
+VCardResponse *vcard_response_new(VCard *card, unsigned char *buf, int len,
+                                  int Le, vcard_7816_status_t status);
+/* response from a return buffer and status bytes */
+VCardResponse *vcard_response_new_bytes(VCard *card, unsigned char *buf,
+                                        int len, int Le,
+                                        unsigned char sw1, unsigned char sw2);
+/* response from just status bytes */
+VCardResponse *vcard_response_new_status_bytes(unsigned char sw1,
+                                               unsigned char sw2);
+/* response from just status: NOTE this cannot fail, it will alwyas return a
+ * valid response, if it can't allocate memory, the response will be
+ * VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE */
+VCardResponse *vcard_make_response(vcard_7816_status_t status);
+
+/* create a raw response (status has already been encoded */
+VCardResponse *vcard_response_new_data(unsigned char *buf, int len);
+
+
+
+
+/*
+ * destructor for VCardResponse.
+ *  Can be called with a NULL response
+ */
+void vcard_response_delete(VCardResponse *response);
+
+/*
+ * constructor for VCardAPDU
+ */
+VCardAPDU *vcard_apdu_new(unsigned char *raw_apdu, int len,
+                          unsigned short *status);
+
+/*
+ * destructor for VCardAPDU
+ *  Can be called with a NULL apdu
+ */
+void vcard_apdu_delete(VCardAPDU *apdu);
+
+/*
+ * APDU processing starts here. This routes the card processing stuff to the
+ * right location. Always returns a valid response.
+ */
+VCardStatus vcard_process_apdu(VCard *card, VCardAPDU *apdu,
+                               VCardResponse **response);
+
+#endif
diff --git a/libcaccard/card_7816t.h b/libcaccard/card_7816t.h
new file mode 100644
index 0000000..531455c
--- /dev/null
+++ b/libcaccard/card_7816t.h
@@ -0,0 +1,163 @@
+/*
+ * Implement the 7816 portion of the card spec
+ *
+ */
+#ifndef CARD_7816T_H
+#define CARD_7816T_H 1
+
+typedef unsigned short vcard_7816_status_t;
+
+struct VCardResponseStruct {
+    unsigned char *b_data;
+    vcard_7816_status_t b_status;
+    unsigned char b_sw1;
+    unsigned char b_sw2;
+    int b_len;
+    int b_total_len;
+    enum VCardResponseBufferType {
+        VCARD_MALLOC,
+        VCARD_MALLOC_DATA,
+        VCARD_MALLOC_STRUCT,
+        VCARD_STATIC
+    } b_type;
+};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS(stat) \
+static const VCardResponse VCardResponse##stat = \
+        {(unsigned char *)&VCardResponse##stat.b_sw1, (stat), ((stat) >> 8), \
+         ((stat) & 0xff), 0, 2, VCARD_STATIC};
+
+#define VCARD_RESPONSE_NEW_STATIC_STATUS_BYTES(sw1, sw2) \
+static const VCardResponse VCARDResponse##sw1 = \
+        {(unsigned char *)&VCardResponse##name.b_sw1, ((sw1) << 8 | (sw2)), \
+         (sw1), (sw2), 0, 2, VCARD_STATIC};
+
+/* cast away the const, callers need may need to 'free' the
+ * result, and const implies that they don't */
+#define VCARD_RESPONSE_GET_STATIC(name) \
+        ((VCardResponse *)(&VCardResponse##name))
+
+typedef enum {
+    VCARD_7816_ISO,
+    VCARD_7816_RFU,
+    VCARD_7816_PTS,
+    VCARD_7816_PROPIETARY
+} VCardAPDUType;
+
+
+/*
+ * 7816 header. All APDU's have this header.
+ * They must be laid out in this order.
+ */
+struct VCardAPDUHeader {
+    unsigned char ah_cla;
+    unsigned char ah_ins;
+    unsigned char ah_p1;
+    unsigned char ah_p2;
+    unsigned char ah_Le;
+    unsigned char ah_body[1]; /* indefinate length */
+};
+
+/*
+ * 7816 APDU structure. The raw bytes are stored in the union and can be
+ * accessed directly through u.data (which is aliased as a_data).
+ *
+ * Names of the fields match the 7816 documentation.
+ */
+struct VCardAPDUStruct {
+    int a_len;                /* length of the whole buffer, including header */
+    int a_Lc;                 /* 7816 Lc (parameter length) value */
+    int a_Le;                 /* 7816 Le (expected result length) value */
+    unsigned char *a_body;    /* pointer to the parameter */
+    int a_channel;            /* decoded channel */
+    int a_secure_messaging;   /* decoded secure messaging type */
+    int a_type;               /* decoded type from cla (top nibble of class) */
+    VCardAPDUType a_gen_type; /* generic type (7816, PROPRIETARY, RFU, etc) */
+    union {
+        struct VCardAPDUHeader *header;
+        unsigned char   *data;
+    } u;
+/* give the subfields a unified look */
+#define a_header u.header
+#define a_data u.data
+#define a_cla a_header->ah_cla /* class */
+#define a_ins a_header->ah_ins /* instruction */
+#define a_p1 a_header->ah_p1   /* parameter 1 */
+#define a_p2 a_header->ah_p2   /* parameter 2 */
+};
+
+/* 7816 status codes */
+#define VCARD7816_STATUS_SUCCESS                              0x9000
+#define VCARD7816_STATUS_WARNING                              0x6200
+#define VCARD7816_STATUS_WARNING_RET_CORUPT                   0x6281
+#define VCARD7816_STATUS_WARNING_BUF_END_BEFORE_LE            0x6282
+#define VCARD7816_STATUS_WARNING_INVALID_FILE_SELECTED        0x6283
+#define VCARD7816_STATUS_WARNING_FCI_FORMAT_INVALID           0x6284
+#define VCARD7816_STATUS_WARNING_CHANGE                       0x6300
+#define VCARD7816_STATUS_WARNING_FILE_FILLED                  0x6381
+#define VCARD7816_STATUS_EXC_ERROR                            0x6400
+#define VCARD7816_STATUS_EXC_ERROR_CHANGE                     0x6500
+#define VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE             0x6581
+#define VCARD7816_STATUS_ERROR_WRONG_LENGTH                   0x6700
+#define VCARD7816_STATUS_ERROR_CLA_NOT_SUPPORTED              0x6800
+#define VCARD7816_STATUS_ERROR_CHANNEL_NOT_SUPPORTED          0x6881
+#define VCARD7816_STATUS_ERROR_SECURE_NOT_SUPPORTED           0x6882
+#define VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED          0x6900
+#define VCARD7816_STATUS_ERROR_COMMAND_INCOMPATIBLE_WITH_FILE 0x6981
+#define VCARD7816_STATUS_ERROR_SECURITY_NOT_SATISFIED         0x6982
+#define VCARD7816_STATUS_ERROR_AUTHENTICATION_BLOCKED         0x6983
+#define VCARD7816_STATUS_ERROR_DATA_INVALID                   0x6984
+#define VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED        0x6985
+#define VCARD7816_STATUS_ERROR_DATA_NO_EF                     0x6986
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_MISSING              0x6987
+#define VCARD7816_STATUS_ERROR_SM_OBJECT_INCORRECT            0x6988
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS               0x6a00
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_IN_DATA       0x6a80
+#define VCARD7816_STATUS_ERROR_FUNCTION_NOT_SUPPORTED         0x6a81
+#define VCARD7816_STATUS_ERROR_FILE_NOT_FOUND                 0x6a82
+#define VCARD7816_STATUS_ERROR_RECORD_NOT_FOUND               0x6a83
+#define VCARD7816_STATUS_ERROR_NO_SPACE_FOR_FILE              0x6a84
+#define VCARD7816_STATUS_ERROR_LC_TLV_INCONSISTENT            0x6a85
+#define VCARD7816_STATUS_ERROR_P1_P2_INCORRECT                0x6a86
+#define VCARD7816_STATUS_ERROR_LC_P1_P2_INCONSISTENT          0x6a87
+#define VCARD7816_STATUS_ERROR_DATA_NOT_FOUND                 0x6a88
+#define VCARD7816_STATUS_ERROR_WRONG_PARAMETERS_2             0x6b00
+#define VCARD7816_STATUS_ERROR_INS_CODE_INVALID               0x6d00
+#define VCARD7816_STATUS_ERROR_CLA_INVALID                    0x6e00
+#define VCARD7816_STATUS_ERROR_GENERAL                        0x6f00
+/* 7816 sw1 codes */
+#define VCARD7816_SW1_SUCCESS               0x90
+#define VCARD7816_SW1_RESPONSE_BYTES        0x61
+#define VCARD7816_SW1_WARNING               0x62
+#define VCARD7816_SW1_WARNING_CHANGE        0x63
+#define VCARD7816_SW1_EXC_ERROR             0x64
+#define VCARD7816_SW1_EXC_ERROR_CHANGE      0x65
+#define VCARD7816_SW1_ERROR_WRONG_LENGTH    0x67
+#define VCARD7816_SW1_CLA_ERROR             0x68
+#define VCARD7816_SW1_COMMAND_ERROR         0x69
+#define VCARD7816_SW1_P1_P2_ERROR           0x6a
+#define VCARD7816_SW1_LE_ERROR              0x6c
+#define VCARD7816_SW1_INS_ERROR             0x6d
+#define VCARD7816_SW1_CLA_NOT_SUPPORTED     0x6e
+
+/* 7816 Instructions */
+#define VCARD7816_INS_MANAGE_CHANNEL        0x70
+#define VCARD7816_INS_EXTERNAL_AUTHENTICATE 0x82
+#define VCARD7816_INS_GET_CHALLENGE         0x84
+#define VCARD7816_INS_INTERNAL_AUTHENTICATE 0x88
+#define VCARD7816_INS_ERASE_BINARY          0x0e
+#define VCARD7816_INS_READ_BINARY           0xb0
+#define VCARD7816_INS_WRITE_BINARY          0xd0
+#define VCARD7816_INS_UPDATE_BINARY         0xd6
+#define VCARD7816_INS_READ_RECORD           0xb2
+#define VCARD7816_INS_WRITE_RECORD          0xd2
+#define VCARD7816_INS_UPDATE_RECORD         0xdc
+#define VCARD7816_INS_APPEND_RECORD         0xe2
+#define VCARD7816_INS_ENVELOPE              0xc2
+#define VCARD7816_INS_PUT_DATA              0xda
+#define VCARD7816_INS_GET_DATA              0xca
+#define VCARD7816_INS_SELECT_FILE           0xa4
+#define VCARD7816_INS_VERIFY                0x20
+#define VCARD7816_INS_GET_RESPONSE          0xc0
+
+#endif
diff --git a/libcaccard/config.h b/libcaccard/config.h
new file mode 100644
index 0000000..19775eb
--- /dev/null
+++ b/libcaccard/config.h
@@ -0,0 +1,81 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Define if building universal (internal helper macro) */
+/* #undef AC_APPLE_UNIVERSAL_BUILD */
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "cac_card"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "cac_card"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "cac_card 0.0.1"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "cac_card"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "0.0.1"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* Version number of package */
+#define VERSION "0.0.1"
+
+/* Define WORDS_BIGENDIAN to 1 if your processor stores words with the most
+   significant byte first (like Motorola and SPARC, unlike Intel). */
+#if defined AC_APPLE_UNIVERSAL_BUILD
+# if defined __BIG_ENDIAN__
+#  define WORDS_BIGENDIAN 1
+# endif
+#else
+# ifndef WORDS_BIGENDIAN
+/* #  undef WORDS_BIGENDIAN */
+# endif
+#endif
diff --git a/libcaccard/event.c b/libcaccard/event.c
new file mode 100644
index 0000000..25d99ae
--- /dev/null
+++ b/libcaccard/event.c
@@ -0,0 +1,112 @@
+/*
+ *
+ */
+#include "vcard.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * OS includes
+ */
+#include <stdlib.h>
+
+/*
+ * from spice
+ */
+#include "mutex.h"
+
+VEvent *
+vevent_new(VEventType type, VReader *reader, VCard *card)
+{
+    VEvent *new_vevent;
+
+    new_vevent = (VEvent *)malloc(sizeof(VEvent));
+    if (new_vevent == NULL) {
+        return NULL;
+    }
+    new_vevent->next = NULL;
+    new_vevent->type = type;
+    new_vevent->reader = vreader_reference(reader);
+    new_vevent->card = vcard_reference(card);
+
+    return new_vevent;
+}
+
+void
+vevent_delete(VEvent *vevent)
+{
+    if (vevent == NULL) {
+        return;
+    }
+    vreader_free(vevent->reader);
+    vcard_free(vevent->card);
+    free(vevent);
+}
+
+/*
+ * VEvent queue management
+ */
+
+static VEvent *vevent_queue_head = NULL;
+static VEvent *vevent_queue_tail = NULL;
+static mutex_t vevent_queue_lock;
+static condition_t vevent_queue_condition;
+
+void vevent_queue_init(void)
+{
+    MUTEX_INIT(vevent_queue_lock);
+    CONDITION_INIT(vevent_queue_condition);
+    vevent_queue_head = vevent_queue_tail = NULL;
+}
+
+void
+vevent_queue_vevent(VEvent *vevent)
+{
+    vevent->next = NULL;
+    MUTEX_LOCK(vevent_queue_lock);
+    if (vevent_queue_head) {
+        assert(vevent_queue_tail);
+        vevent_queue_tail->next = vevent;
+    } else {
+        vevent_queue_head = vevent;
+    }
+    vevent_queue_tail = vevent;
+    CONDITION_NOTIFY(vevent_queue_condition);
+    MUTEX_UNLOCK(vevent_queue_lock);
+}
+
+/* must have lock */
+static VEvent *
+vevent_dequeue_vevent(void)
+{
+    VEvent *vevent = NULL;
+    if (vevent_queue_head) {
+        vevent = vevent_queue_head;
+        vevent_queue_head = vevent->next;
+        vevent->next = NULL;
+    }
+    return vevent;
+}
+
+VEvent * vevent_wait_next_vevent(void)
+{
+    VEvent *vevent;
+
+    MUTEX_LOCK(vevent_queue_lock);
+    while ((vevent = vevent_dequeue_vevent()) == NULL) {
+        CONDITION_WAIT(vevent_queue_condition, vevent_queue_lock);
+    }
+    MUTEX_UNLOCK(vevent_queue_lock);
+    return vevent;
+}
+
+VEvent * vevent_get_next_vevent(void)
+{
+    VEvent *vevent;
+
+    MUTEX_LOCK(vevent_queue_lock);
+    vevent = vevent_dequeue_vevent();
+    MUTEX_UNLOCK(vevent_queue_lock);
+    return vevent;
+}
+
diff --git a/libcaccard/eventt.h b/libcaccard/eventt.h
new file mode 100644
index 0000000..4c3df4f
--- /dev/null
+++ b/libcaccard/eventt.h
@@ -0,0 +1,28 @@
+/*
+ *
+ */
+
+#ifndef EVENTT_H
+#define EVENTT_H 1
+#include "vreadert.h"
+#include "vcardt.h"
+
+typedef struct VEventStruct VEvent;
+
+typedef enum {
+    VEVENT_READER_INSERT,
+    VEVENT_READER_REMOVE,
+    VEVENT_CARD_INSERT,
+    VEVENT_CARD_REMOVE,
+    VEVENT_LAST,
+} VEventType;
+
+struct VEventStruct {
+    VEvent *next;
+    VEventType type;
+    VReader *reader;
+    VCard *card;
+};
+#endif
+
+
diff --git a/libcaccard/link_test.c b/libcaccard/link_test.c
new file mode 100644
index 0000000..40d2c6d
--- /dev/null
+++ b/libcaccard/link_test.c
@@ -0,0 +1,20 @@
+/*
+ *
+ */
+#include <stdio.h>
+#include "vcard.h"
+
+VCardStatus cac_card_init(const char *flags, VCard *card,
+                const unsigned char *cert[],
+                int cert_len[], VCardKey *key[] /* adopt the keys*/,
+                int cert_count);
+/*
+ * this will crash... just test the linkage right now
+ */
+
+main(int argc, char **argv)
+{
+    VCard *card; /* no constructor yet */
+    cac_card_init("", card, NULL, 0, NULL, 0);
+}
+
diff --git a/libcaccard/mutex.h b/libcaccard/mutex.h
new file mode 100644
index 0000000..db44814
--- /dev/null
+++ b/libcaccard/mutex.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*- */
+/*
+   Copyright (C) 2009 Red Hat, Inc.
+
+   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, see <http://www.gnu.org/licenses/>.
+*/
+
+/*
+ *  This header file provides a way of mapping windows and linux thread calls
+ *  to a set of macros.  Ideally this would be shared by whatever subsystem we
+ *  link with.
+ */
+
+#ifndef _H_MUTEX
+#define _H_MUTEX
+#ifdef _WIN32
+#include <windows.h>
+typedef CRITICAL_SECTION mutex_t;
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+typedef CONDITION_VARIABLE condition_t;
+#define CONDITION_INIT(cond) InitializeConditionVariable(&cond)
+#define CONDITION_WAIT(cond,mutex) \
+            SleepConditionVariableCS(&cond,&mutex,INFINTE)
+#define CONDITION_NOTIFY(cond) WakeConditionVariable(&cond)
+typedef uint32_t thread_t;
+typedef HANDLE thread_status_t;
+#define THREAD_CREATE(tid, func, arg) \
+        CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)func, arg, 0, &tid)
+#define THREAD_SUCCESS(status) ((status) !=  NULL)
+#else
+#include <pthread.h>
+typedef pthread_mutex_t mutex_t;
+#define MUTEX_INIT(mutex) pthread_mutex_init(&mutex, NULL)
+#define MUTEX_LOCK(mutex) pthread_mutex_lock(&mutex)
+#define MUTEX_UNLOCK(mutex) pthread_mutex_unlock(&mutex)
+typedef pthread_cond_t condition_t;
+#define CONDITION_INIT(cond) pthread_cond_init(&cond, NULL)
+#define CONDITION_WAIT(cond,mutex) pthread_cond_wait(&cond,&mutex)
+#define CONDITION_NOTIFY(cond) pthread_cond_signal(&cond)
+typedef pthread_t thread_t;
+typedef int thread_status_t;
+#define THREAD_CREATE(tid, func, arg) pthread_create(&tid, NULL, func, arg)
+#define THREAD_SUCCESS(status)  ((status) == 0)
+#endif
+
+#endif // _H_MUTEX
diff --git a/libcaccard/passthru.c b/libcaccard/passthru.c
new file mode 100644
index 0000000..5c2c7bd
--- /dev/null
+++ b/libcaccard/passthru.c
@@ -0,0 +1,608 @@
+/*
+ * implement the applets for the CAC card.
+ */
+#ifdef USE_PASSTHRU
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "mutex.h"
+#include "vcard_emul.h"
+#include "passthru.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pcsclite.h>
+
+/*
+ * Passthru applet private data
+ */
+struct VCardAppletPrivateStruct {
+    char *reader_name;
+    /* pcsc-lite parameters */
+    SCARDHANDLE hCard;
+    uint32_t hProtocol;
+    SCARD_IO_REQUEST *send_io;
+    unsigned char atr[MAX_ATR_SIZE];
+    int atr_len;
+};
+
+static SCARDCONTEXT global_context = 0;
+
+#define MAX_RESPONSE_LENGTH 261 /*65537 */
+/*
+ * handle all the APDU's that are common to all CAC applets
+ */
+static VCardStatus
+passthru_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+    LONG rv;
+    unsigned char buf[MAX_RESPONSE_LENGTH];
+    uint32_t len = MAX_RESPONSE_LENGTH;
+    VCardAppletPrivate *applet_private = NULL;
+    SCARD_IO_REQUEST receive_io;
+
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if (applet_private == NULL) {
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    rv = SCardTransmit(applet_private->hCard, applet_private->send_io,
+                       apdu->a_data, apdu->a_len, &receive_io, buf, &len);
+    if (rv != SCARD_S_SUCCESS) {
+       *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR);
+       return VCARD_DONE;
+    }
+
+    *response = vcard_response_new_data(buf,len);
+    if (*response == NULL) {
+       *response =
+            vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+    } else {
+       (*response)->b_total_len = (*response)->b_len;
+    }
+    return VCARD_DONE;
+}
+
+static void
+passthru_card_set_atr(VCard *card, unsigned char *atr, int atr_len)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if (applet_private == NULL) {
+        return;
+    }
+    applet_private->atr_len = MIN(atr_len, sizeof(applet_private->atr));
+    memcpy(applet_private->atr, atr, applet_private->atr_len);
+}
+
+static void passthru_card_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    SCARD_READERSTATE *state;
+
+    applet_private = vcard_get_current_applet_private(card, 0);
+    if ((applet_private == NULL) || (applet_private->atr_len == 0)) {
+        vcard_emul_get_atr(card, atr, atr_len);
+        return;
+    }
+    *atr_len = MIN(applet_private->atr_len, *atr_len);
+    memcpy(atr,applet_private->atr,*atr_len);
+    return;
+}
+
+/*
+ *  resest the inter call state between applet selects
+ */
+static VCardStatus
+passthru_reset(VCard *card, int channel)
+{
+    return VCARD_DONE;
+}
+
+static VCardStatus
+passthru_pcsc_lite_init()
+{
+    LONG rv;
+    if (global_context != 0) {
+        return VCARD_DONE;
+    }
+    rv = SCardEstablishContext(SCARD_SCOPE_SYSTEM, NULL, NULL, &global_context);
+    if (rv != SCARD_S_SUCCESS) {
+        return VCARD_FAIL;
+    }
+    return VCARD_DONE;
+}
+
+/*
+ *  match if s1 is completely contained in s2
+ */
+static int
+string_match(const char *s1, const char *s2)
+{
+    int len = strlen(s1);
+    const char *start;
+
+    for (start = strchr(s2, *s1); start; start = strchr(start+1, *s1)) {
+       if (strncmp(start, s1, len) == 0) {
+           return 1;
+       }
+    }
+    return 0;
+}
+
+
+/*
+ *  Look for the reader that best matches the name for VReader
+ */
+static char *
+passthru_get_reader_name(VReader *reader)
+{
+    const char *reader_name = vreader_get_name(reader);
+    char *reader_list = NULL;
+    char *reader_entry = NULL;
+    char *reader_match = NULL;
+    uint32_t reader_string_length;
+    VCardStatus status;
+    LONG rv;
+
+    if (reader_name == NULL) {
+        return NULL;
+    }
+
+    status = passthru_pcsc_lite_init();
+    if (status != VCARD_DONE) {
+       return NULL;
+    }
+
+
+    /* find the existing reader names */
+    rv = SCardListReaders(global_context, NULL, NULL, &reader_string_length);
+    if (rv !=  SCARD_S_SUCCESS) {
+       return NULL;
+    }
+    reader_list = (char *)malloc(reader_string_length);
+    rv = SCardListReaders(global_context, NULL, reader_list,
+                          &reader_string_length);
+    if (rv !=  SCARD_S_SUCCESS) {
+       goto cleanup;
+    }
+
+    /* match that name */
+    for (reader_entry= reader_list;*reader_entry;
+                                   reader_entry += strlen(reader_entry)+1) {
+       if (string_match(reader_entry, reader_name)) {
+           reader_match = strdup(reader_entry);
+           break;
+       }
+    }
+cleanup:
+    if (reader_list) {
+        free(reader_list);
+    }
+    return reader_match;
+}
+
+
+/*
+ * utilities for creating and destroying the private applet data
+ */
+static void
+passthru_delete_applet_private(VCardAppletPrivate *applet_private)
+{
+    if (applet_private == NULL) {
+        return;
+    }
+    if (applet_private->hCard) {
+        SCardDisconnect(applet_private->hCard,SCARD_LEAVE_CARD);
+    }
+    if (applet_private->reader_name != NULL) {
+        free(applet_private->reader_name);
+    }
+    free(applet_private);
+}
+
+static VCardAppletPrivate *
+passthru_new_applet_private(VReader *reader)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    LONG rv;
+
+    applet_private = (VCardAppletPrivate *)malloc(sizeof(VCardAppletPrivate));
+
+    if (applet_private == NULL) {
+        goto fail;
+    }
+    applet_private->hCard = 0;
+    applet_private->reader_name = NULL;
+
+    applet_private->reader_name = passthru_get_reader_name(reader);
+    if (applet_private->reader_name == NULL) {
+        goto fail;
+    }
+
+    rv = SCardConnect( global_context, applet_private->reader_name,
+       SCARD_SHARE_DIRECT, SCARD_PROTOCOL_T0|SCARD_PROTOCOL_T1,
+        &applet_private->hCard,
+        &applet_private->hProtocol);
+    if (rv !=  SCARD_S_SUCCESS) {
+        goto fail;
+    }
+
+    if (applet_private->hProtocol == SCARD_PROTOCOL_T0) {
+        applet_private->send_io = SCARD_PCI_T0;
+    } else {
+        applet_private->send_io = SCARD_PCI_T1;
+    }
+    applet_private->atr_len = 0;
+    return applet_private;
+
+fail:
+    if (applet_private) {
+        passthru_delete_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+/*
+ * create a new applet which links to our override function.
+ */
+static VCardApplet *
+passthru_new_applet(VReader *reader)
+{
+    VCardAppletPrivate *applet_private = NULL;
+    VCardApplet *applet = NULL;
+    unsigned char passthru_aid[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+    int passthru_aid_len = sizeof (passthru_aid);
+
+    applet_private = passthru_new_applet_private(reader);
+    if (applet_private == NULL) {
+        goto failure;
+    }
+    applet = vcard_new_applet(passthru_process_apdu, passthru_reset,
+                              passthru_aid, passthru_aid_len);
+    if (applet == NULL) {
+        goto failure;
+    }
+    vcard_set_applet_private(applet, applet_private,
+                          passthru_delete_applet_private);
+    applet_private = NULL;
+
+    return applet;
+
+failure:
+    if (applet_private != NULL) {
+        passthru_delete_applet_private(applet_private);
+    }
+    return NULL;
+}
+
+
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers.
+ */
+VCardStatus
+passthru_card_init(VReader *vreader, VCard *card,
+              const char *flags,
+              unsigned char * const *cert,
+              int cert_len[],
+              VCardKey *key[] /* adopt the keys*/,
+              int cert_count)
+{
+    int i;
+    VCardApplet *applet;
+
+    /* Don't do soft emulation of the 7816, pass everything to the card */
+    vcard_set_type(card,VCARD_DIRECT);
+
+    applet = passthru_new_applet(vreader);
+    if (applet == NULL) {
+        goto failure;
+    }
+
+    vcard_add_applet(card, applet);
+
+    /* we are adopting the keys, so free them now (since we don't use them) */
+    for (i=0; i < cert_count; i++) {
+      vcard_emul_delete_key(key[i]);
+    }
+
+    return VCARD_DONE;
+
+failure:
+    return VCARD_FAIL;
+}
+
+/*
+ *  Begin passthru_emul code. This emulator only works with the passthru card
+ *  type.
+ *
+ */
+
+/*
+ *  Get the state entry that matches this reader. If none found, return NULL
+ */
+static SCARD_READERSTATE_A *
+passthru_get_reader_state(SCARD_READERSTATE_A *reader_states,
+                          int reader_count, char *name)
+{
+    int i;
+
+    for (i=0; i < reader_count; i++) {
+        if (name == NULL || reader_states[i].szReader == NULL) {
+            continue;
+        }
+        if (strcmp(name, reader_states[i].szReader) == 0) {
+            return &reader_states[i];
+        }
+    }
+    return NULL;
+}
+
+/*
+ * find a card slot that has been cleared out
+ */
+static SCARD_READERSTATE_A *
+passthru_get_blank_reader(SCARD_READERSTATE_A *reader_states, int reader_count)
+{
+    return passthru_get_reader_state(reader_states,  reader_count, NULL);
+}
+
+
+/*
+ *  This is the main work of the emulator, handling the thread that looks for
+ *  changes in the readers and the cards.
+ */
+static void *
+passthru_emul_event_thread(void *args)
+{
+    char *reader_list = NULL;
+    int reader_list_len = 0;
+    SCARD_READERSTATE_A *reader_states = NULL;
+    int reader_count = 0;     /* number of active readers */
+    int max_reader_count = 0; /* size of the reader_state array (including
+                                 inactive readers) */
+    LONG rv;
+    int timeout=1000;
+    int i;
+
+    do {
+        /* variables to hold on to our new values until we are ready to replace
+         * our old values */
+        char *new_reader_list = NULL;
+        int new_reader_list_len = 0;
+        int new_reader_count = 0;
+
+        /* other temps */
+        char * reader_entry;
+        VReader *reader;
+
+        /*
+         * First check to see if the reader list has changed
+         */
+        rv = SCardListReaders(global_context, NULL, NULL, &new_reader_list_len);
+        if (rv !=  SCARD_S_SUCCESS) {
+           goto next;
+        }
+        /*
+         * If the names have changed, we need to update our list and states.
+         * This is where we detect reader insertions and removals.
+         */
+        if (new_reader_list_len != reader_list_len) {
+            /* update the list */
+            new_reader_list = (char *)malloc(new_reader_list_len);
+            if (new_reader_list == NULL) {
+                goto next;
+            }
+            rv = SCardListReaders(global_context, NULL, new_reader_list,
+                                  &new_reader_list_len);
+            if (rv !=  SCARD_S_SUCCESS) {
+                free(new_reader_list);
+                goto next;
+            }
+            /* clear out our event state */
+            for (i=0; i < reader_count; i++) {
+                    reader_states[i].dwEventState = 0;
+            }
+            /* count the readers and mark the ones that are still with us */
+            for (reader_entry = new_reader_list; *reader_entry;
+                 reader_entry += strlen(reader_entry)+1) {
+                SCARD_READERSTATE_A *this_state;
+                new_reader_count++;
+                /* if the reader is still on the list, mark it present */
+                this_state = passthru_get_reader_state(reader_states,
+                                                       reader_count,
+                                                       reader_entry);
+                if (this_state) {
+                    this_state->dwEventState = SCARD_STATE_PRESENT;
+                }
+            }
+            /* eject any removed readers */
+            for (i=0; i < reader_count; i++) {
+                if (reader_states[i].dwEventState == SCARD_STATE_PRESENT) {
+                    reader_states[i].dwEventState = 0;
+                    continue;
+                }
+                reader = vreader_get_reader_by_name(reader_states[i].szReader);
+                vreader_remove_reader(reader);
+                vreader_free(reader);
+                reader_states[i].szReader = NULL;
+            }
+            /* handle the shrinking list */
+            if (new_reader_count < reader_count) {
+                /* fold all the valid entries at the end of our reader_states
+                 * array up into those locations vacated by ejected readers. */
+                for (i=reader_count-1; i < (new_reader_count -1); i--) {
+                        if (reader_states[i].szReader) {
+                            SCARD_READERSTATE_A *blank_reader;
+                            blank_reader =
+                                passthru_get_blank_reader(reader_states,
+                                                          new_reader_count);
+                            assert(blank_reader);
+                            *blank_reader = reader_states[i];
+                            reader_states[i].szReader = NULL;
+                        }
+                 }
+            }
+            /* handle the growing list */
+            if (new_reader_count >  max_reader_count) {
+                SCARD_READERSTATE_A *new_reader_states;
+
+                /* grow the list */
+                new_reader_states =
+                    (SCARD_READERSTATE_A *)realloc(reader_states,
+                        sizeof(SCARD_READERSTATE_A)*new_reader_count);
+                if (new_reader_states) {
+                    /* successful, update our current state */
+                    reader_states = new_reader_states;
+                    max_reader_count = new_reader_count;
+                } else {
+                    new_reader_count = max_reader_count; /* couldn't get enough
+                                                          * space to handle
+                                                          * all the new readers
+                                                          * */
+                }
+                /* mark our new entries as empty */
+                for (i=reader_count; i > new_reader_count; i++) {
+                    reader_states[i].szReader = NULL;
+                }
+            }
+            /* now walk the reader list, updating the state */
+            for (reader_entry = new_reader_list; *reader_entry;
+                 reader_entry += strlen(reader_entry)+1) {
+                SCARD_READERSTATE_A *this_state;
+                this_state = passthru_get_reader_state(reader_states,
+                                                       new_reader_count,
+                                                       reader_entry);
+                if (this_state) {
+                    /* replace the old copy of the string with the new copy.
+                     * This will allow us to free reader_list at the end */
+                    reader_states->szReader = reader_entry;
+                    continue;
+                }
+                /* this is a new reader, add it to the list */
+                this_state =
+                    passthru_get_blank_reader(reader_states, new_reader_count);
+                if (!this_state) {
+                    continue; /* this can happen of we couldn't get enough
+                                 slots in the grow list */
+                }
+                this_state->szReader = reader_entry;
+                this_state->dwCurrentState = SCARD_STATE_UNAWARE;
+                reader = vreader_new(reader_entry, NULL, NULL);
+                if (reader) {
+                    vreader_add_reader(reader);
+                }
+                vreader_free(reader);
+            }
+            /* finally update our current variables */
+            free(reader_list);
+            reader_list = new_reader_list;
+            reader_list_len = new_reader_list_len;
+            reader_count = new_reader_count;
+        }
+next:
+        rv = SCardGetStatusChange(global_context, timeout,
+                                  reader_states, reader_count);
+        if (rv == SCARD_E_TIMEOUT) {
+            continue; /* check for new readers */
+        }
+        if (rv != SCARD_S_SUCCESS) {
+            static int restarts = 0;
+            VCardStatus status;
+
+            /* try resetting the pcsc_lite subsystem */
+            SCardReleaseContext(global_context);
+            global_context = 0; /* should close it */
+            printf("***** SCard failure %x\n", rv);
+            restarts++;
+            if (restarts >= 3) {
+                printf("***** SCard failed %d times\n", restarts);
+                return; /* exit thread */
+            }
+            status = passthru_pcsc_lite_init();
+            assert(status == CARD_DONE);
+            sleep(1);
+            continue;
+        }
+        /* deal with card insertion/removal */
+        for (i=0; i < reader_count ; i++) {
+            if ((reader_states[i].dwEventState & SCARD_STATE_CHANGED) == 0) {
+                continue;
+            }
+            reader_states[i].dwCurrentState = reader_states[i].dwEventState;
+            reader = vreader_get_reader_by_name(reader_states[i].szReader);
+            if (reader == NULL) {
+                continue;
+            }
+            if (reader_states[i].dwEventState & SCARD_STATE_EMPTY) {
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    vreader_insert_card(reader, NULL);
+                }
+            }
+            if (reader_states[i].dwEventState & SCARD_STATE_PRESENT) {
+                VCard *card;
+                VCardStatus status = VCARD_FAIL;
+                /* if there already was a card present, eject it before we
+                 * insert the new one */
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    vreader_insert_card(reader, NULL);
+                }
+
+                card = vcard_new(NULL, NULL);
+                if (card != NULL) {
+                    status = passthru_card_init(reader, card, "",
+                                                NULL, NULL, NULL, 0);
+                    passthru_card_set_atr(card, reader_states[i].rgbAtr,
+                                  reader_states[i].cbAtr);
+                    vcard_set_atr_func(card, passthru_card_get_atr);
+                }
+                if (status == VCARD_DONE) {
+                    vreader_insert_card(reader, card);
+                }
+                vcard_free(card);
+            }
+            vreader_free(reader);
+        }
+
+     } while (1);
+     return NULL;
+}
+
+/*
+ *  Initializing the passthru emul is simply initializing pcsc-lite and
+ *  launching the event thread.
+ */
+VCardStatus
+passthru_emul_init(VCardEmulOptions *options)
+{
+    thread_t tid;
+    thread_status_t tstatus;
+    VCardStatus status;
+
+    vreader_init();
+    vevent_queue_init();
+
+    status = passthru_pcsc_lite_init();
+    if (status != VCARD_DONE) {
+        return status;
+    }
+
+    /* launch reader thread */
+    tstatus = THREAD_CREATE(tid, passthru_emul_event_thread, NULL);
+    if (!THREAD_SUCCESS(tstatus)) {
+        return VCARD_FAIL;
+    }
+    return VCARD_DONE;
+}
+
+
+VCardEmulOptions *
+passthru_emul_options(const char *args)
+{
+    return NULL;
+}
+#endif
diff --git a/libcaccard/passthru.h b/libcaccard/passthru.h
new file mode 100644
index 0000000..3589d12
--- /dev/null
+++ b/libcaccard/passthru.h
@@ -0,0 +1,50 @@
+/*
+ * passthru card type emulator and passhtru emulator.
+ *
+ * passhtru card type emulator can be used with other low level card emulators,
+ * as long as they can recognize card insertion and removals.
+ *
+ * the passthru vcard_emulator, can only use passthru card types.
+ *
+ * Be careful using passthru. 1) passthru does not know the locking state of
+ * the card from the guest side, and thus does not try to get locks. This means
+ * client access can interfere with the guest use of the card. 2) passthru does
+ * not provide the guest and client unique login states for the card. That
+ * means that it is possible for the guest to access private data on the
+ * card without authenticating. You have been warned.
+ *
+ * Passthru is most useful in the following cases: 1) provisioning. Card type
+ *  emulators cannot emulate the open platform secure connections because the
+ *  client software does not have access to the global platform keys on the
+ *  card. Passthru drives these apdu's directly to the card. 2) odd cards. If
+ *  you have guest software the knows how to access the card, but no client
+ *  side PKCS #11 module, then passthru can provide access to those cards.
+ */
+
+#ifndef PASSTHRU_H
+#define PASSTHRU_H 1
+
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+
+/*
+ * Initialize the card. This is the only 'card type emulator' portion of this
+ * the rest are connected through function pointers. NOTE: certs are ignored,
+ * keys are freed.
+ */
+VCardStatus passthru_card_init(VReader *vreader, VCard *card,
+              const char *flags, unsigned char * const *cert, int cert_len[],
+              VCardKey *key[], int cert_count);
+
+/*
+ * Use this instead of vcard_emul_init to initialize passthru.
+ * passthru is the exception to the rule that only one emul can be compiled
+ * at once. NOTE: you can still have only one emul active at once. The
+ * passhtru card type emul, however can be used with other emuls.
+ *
+ * passthru does not support other card type emuls.
+ */
+VCardStatus passthru_emul_init(VCardEmulOptions *options);
+VCardEmulOptions *passthru_emul_options(const char *args);
+#endif
diff --git a/libcaccard/vcard.c b/libcaccard/vcard.c
new file mode 100644
index 0000000..9672001
--- /dev/null
+++ b/libcaccard/vcard.c
@@ -0,0 +1,350 @@
+/*
+ * implement the Java card standard.
+ *
+ */
+#include <stdlib.h>
+#include <string.h>
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816t.h"
+
+struct VCardAppletStruct {
+    VCardApplet   *next;
+    VCardProcessAPDU process_apdu;
+    VCardResetApplet reset_applet;
+    unsigned char *aid;
+    int aid_len;
+    void *applet_private;
+    VCardAppletPrivateFree applet_private_free;
+};
+
+struct VCardStruct {
+    int reference_count;
+    VCardApplet *applet_list;
+    VCardApplet *current_applet[MAX_CHANNEL];
+    VCardBufferResponse *vcard_buffer_response;
+    VCardType type;
+    VCardEmul *vcard_private;
+    VCardEmulFree vcard_private_free;
+    VCardGetAtr vcard_get_atr;
+};
+
+VCardBufferResponse *
+vcard_buffer_response_new(unsigned char *buffer, int size)
+{
+    VCardBufferResponse *new_buffer;
+
+    new_buffer = (VCardBufferResponse *)malloc(sizeof(VCardBufferResponse));
+    if (new_buffer == NULL) {
+        return NULL;
+    }
+    new_buffer->buffer = (unsigned char *)malloc(size);
+    if (new_buffer->buffer == NULL) {
+        free(new_buffer);
+        return NULL;
+    }
+    memcpy(new_buffer->buffer, buffer, size);
+    new_buffer->buffer_len = size;
+    new_buffer->current = new_buffer->buffer;
+    new_buffer->len = size;
+    return new_buffer;
+}
+
+void
+vcard_buffer_response_delete(VCardBufferResponse *buffer_response)
+{
+    if (buffer_response == NULL) {
+        return;
+    }
+    if (buffer_response->buffer) {
+        free(buffer_response->buffer);
+    }
+    free(buffer_response);
+}
+
+
+/*
+ * clean up state after a reset
+ */
+void
+vcard_reset(VCard *card, VCardPower power)
+{
+    int i;
+    VCardApplet *applet = NULL;
+
+    if (card->type ==  VCARD_DIRECT) {
+       /* select the last applet */
+        VCardApplet *current_applet = NULL;
+        for (current_applet = card->applet_list; current_applet;
+                                       current_applet = current_applet->next) {
+           applet = current_applet;
+       }
+    }
+    for (i=0; i < MAX_CHANNEL; i++) {
+        card->current_applet[i] = applet;
+    }
+    if (card->vcard_buffer_response) {
+        vcard_buffer_response_delete(card->vcard_buffer_response);
+        card->vcard_buffer_response = NULL;
+    }
+    vcard_emul_reset(card, power);
+    if (applet) {
+        applet->reset_applet(card, 0);
+    }
+}
+
+/* applet utilities */
+
+/*
+ * applet utilities
+ */
+/* constructor */
+VCardApplet *
+vcard_new_applet(VCardProcessAPDU applet_process_function,
+                 VCardResetApplet applet_reset_function,
+                 unsigned char *aid, int aid_len)
+{
+    VCardApplet *applet;
+
+    applet = (VCardApplet *)malloc(sizeof(VCardApplet));
+    if (applet == NULL) {
+        return NULL;
+    }
+    applet->next = NULL;
+    applet->applet_private = NULL;
+    applet->applet_private_free = NULL;
+    applet->process_apdu = applet_process_function;
+    applet->reset_applet = applet_reset_function;
+
+    applet->aid = malloc(aid_len);
+    if (applet->aid == NULL) {
+        free(applet);
+        return NULL;
+    }
+    memcpy(applet->aid, aid, aid_len);
+    applet->aid_len = aid_len;
+    return applet;
+}
+
+/* destructor */
+void
+vcard_delete_applet(VCardApplet *applet)
+{
+    if (applet == NULL) {
+        return;
+    }
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+        applet->applet_private = NULL;
+    }
+    if (applet->aid) {
+        free(applet->aid);
+        applet->aid = NULL;
+    }
+    free(applet);
+}
+
+/* accessor */
+void
+vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *private,
+                         VCardAppletPrivateFree private_free)
+{
+    if (applet->applet_private_free) {
+        applet->applet_private_free(applet->applet_private);
+    }
+    applet->applet_private = private;
+    applet->applet_private_free = private_free;
+}
+
+VCard *
+vcard_new(VCardEmul *private, VCardEmulFree private_free)
+{
+    VCard *new_card;
+    int i;
+
+    new_card = (VCard *)malloc(sizeof(VCard));
+    new_card->applet_list = NULL;
+    for (i=0; i < MAX_CHANNEL; i++) {
+        new_card->current_applet[i] = NULL;
+    }
+    new_card->vcard_buffer_response = NULL;
+    new_card->type = VCARD_VM;
+    new_card->vcard_private = private;
+    new_card->vcard_private_free = private_free;
+    new_card->vcard_get_atr = NULL;
+    new_card->reference_count = 1;
+    return new_card;
+}
+
+VCard *
+vcard_reference(VCard *vcard)
+{
+    if (vcard == NULL) {
+        return NULL;
+    }
+    vcard->reference_count++;
+    return vcard;
+}
+
+void
+vcard_free(VCard *vcard)
+{
+    VCardApplet *current_applet = NULL;
+    VCardApplet *next_applet = NULL;
+
+    if (vcard == NULL) {
+        return;
+    }
+    vcard->reference_count--;
+    if (vcard->reference_count != 0) {
+        return;
+    }
+    if (vcard->vcard_private_free) {
+        (*vcard->vcard_private_free)(vcard->vcard_private);
+        vcard->vcard_private_free = 0;
+        vcard->vcard_private = 0;
+    }
+    for (current_applet = vcard->applet_list; current_applet;
+                                        current_applet = next_applet) {
+        next_applet = current_applet->next;
+        vcard_delete_applet(current_applet);
+    }
+    vcard_buffer_response_delete(vcard->vcard_buffer_response);
+    free(vcard);
+    return;
+}
+
+void
+vcard_get_atr(VCard *vcard, unsigned char *atr, int *atr_len)
+{
+    if (vcard->vcard_get_atr) {
+       (*vcard->vcard_get_atr)(vcard, atr, atr_len);
+       return;
+    }
+    vcard_emul_get_atr(vcard, atr, atr_len);
+}
+
+void
+vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr)
+{
+    card-> vcard_get_atr = vcard_get_atr;
+}
+
+
+VCardStatus
+vcard_add_applet(VCard *card, VCardApplet *applet)
+{
+    applet->next = card->applet_list;
+    card->applet_list = applet;
+    /* if our card-type is direct, always call the applet */
+    if (card->type ==  VCARD_DIRECT) {
+        int i;
+
+        for (i=0; i < MAX_CHANNEL; i++) {
+            card->current_applet[i] = applet;
+        }
+    }
+    return VCARD_DONE;
+}
+
+/*
+ * manage applets
+ */
+VCardApplet *
+vcard_find_applet(VCard *card, unsigned char *aid, int aid_len)
+{
+    VCardApplet *current_applet;
+
+    for (current_applet = card->applet_list; current_applet;
+                                        current_applet = current_applet->next) {
+        if (current_applet->aid_len != aid_len) {
+            continue;
+        }
+        if (memcmp(current_applet->aid, aid, aid_len) == 0) {
+            break;
+        }
+    }
+    return current_applet;
+}
+
+unsigned char *
+vcard_applet_get_aid(VCardApplet *applet, int *aid_len)
+{
+    if (applet == NULL) {
+        return NULL;
+    }
+    *aid_len = applet->aid_len;
+    return applet->aid;
+}
+
+
+void
+vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
+{
+    ASSERT(channel < MAX_CHANNEL);
+    card->current_applet[channel] = applet;
+    /* reset the applet */
+    if (applet && applet->reset_applet) {
+        applet->reset_applet(card, channel);
+    }
+}
+
+VCardAppletPrivate *
+vcard_get_current_applet_private(VCard *card, int channel)
+{
+    VCardApplet *applet = card->current_applet[channel];
+
+    if (applet == NULL) {
+        return NULL;
+    }
+    return applet->applet_private;
+}
+
+VCardStatus
+vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                          VCardResponse **response)
+{
+    if (card->current_applet[apdu->a_channel]) {
+        return card->current_applet[apdu->a_channel]->process_apdu(
+                                                        card, apdu, response);
+    }
+    return VCARD_NEXT;
+}
+
+/*
+ * Accessor functions
+ */
+/* accessor functions for the response buffer */
+VCardBufferResponse *
+vcard_get_buffer_response(VCard *card)
+{
+    return card->vcard_buffer_response;
+}
+
+void
+vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer)
+{
+    card->vcard_buffer_response = buffer;
+}
+
+
+/* accessor functions for the type */
+VCardType
+vcard_get_type(VCard *card)
+{
+    return card->type;
+}
+
+void
+vcard_set_type(VCard *card, VCardType type)
+{
+    card->type = type;
+}
+
+/* accessor for private data */
+VCardEmul *
+vcard_get_private(VCard *vcard)
+{
+    return vcard->vcard_private;
+}
+
diff --git a/libcaccard/vcard.h b/libcaccard/vcard.h
new file mode 100644
index 0000000..8dbd761
--- /dev/null
+++ b/libcaccard/vcard.h
@@ -0,0 +1,85 @@
+/*
+ *
+ */
+#ifndef VCARD_H
+#define VCARD_H 1
+
+#include "vcardt.h"
+
+/*
+ * response buffer constructors and destructors.
+ *
+ * response buffers are used when we need to return more data than will fit in
+ * a normal APDU response (nominally 254 bytes).
+ */
+VCardBufferResponse *vcard_buffer_response_new(unsigned char *buffer, int size);
+void vcard_buffer_response_delete(VCardBufferResponse *buffer_response);
+
+
+/*
+ * clean up state on reset
+ */
+void vcard_reset(VCard *card, VCardPower power);
+
+/*
+ * applet utilities
+ */
+/*
+ * Constructor for a VCardApplet
+ */
+VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
+                              VCardResetApplet applet_reset_function,
+                              unsigned char *aid, int aid_len);
+
+/*
+ * destructor for a VCardApplet
+ *  Can be called with a NULL applet
+ */
+void vcard_delete_applet(VCardApplet *applet);
+
+/* accessor - set the card type specific private data */
+void vcard_set_applet_private(VCardApplet *applet, VCardAppletPrivate *_private,
+                              VCardAppletPrivateFree private_free);
+
+/* set type of vcard */
+void vcard_set_type(VCard *card, VCardType type);
+
+/*
+ * utilities interacting with the current applet
+ */
+/* add a new applet to a card */
+VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
+/* find the applet on the card with the given aid */
+VCardApplet *vcard_find_applet(VCard *card, unsigned char *aid, int aid_len);
+/* set the following applet to be current on the given channel */
+void vcard_select_applet(VCard *card, int channel, VCardApplet *applet);
+/* get the card type specific private data on the given channel */
+VCardAppletPrivate * vcard_get_current_applet_private(VCard *card, int channel);
+/* fetch the applet's id */
+unsigned char *vcard_applet_get_aid(VCardApplet *applet, int *aid_len);
+
+/* process the apdu for the current selected applet/file */
+VCardStatus vcard_process_applet_apdu(VCard *card, VCardAPDU *apdu,
+                                      VCardResponse **response);
+/*
+ * VCard utilities
+ */
+/* constructor */
+VCard * vcard_new(VCardEmul *_private, VCardEmulFree private_free);
+/* get a reference */
+VCard * vcard_reference(VCard *);
+/* destructor (reference counted) */
+void vcard_free(VCard *);
+/* get the atr from the card */
+void vcard_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+void vcard_set_atr_func(VCard *card, VCardGetAtr vcard_get_atr);
+
+/* accessor functions for the response buffer */
+VCardBufferResponse *vcard_get_buffer_response(VCard *card);
+void vcard_set_buffer_response(VCard *card, VCardBufferResponse *buffer);
+/* accessor functions for the type */
+VCardType vcard_get_type(VCard *card);
+/* get the private data */
+VCardEmul *vcard_get_private(VCard *card);
+
+#endif
diff --git a/libcaccard/vcard_emul.h b/libcaccard/vcard_emul.h
new file mode 100644
index 0000000..749267a
--- /dev/null
+++ b/libcaccard/vcard_emul.h
@@ -0,0 +1,59 @@
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ */
+
+#ifndef VCARD_EMUL_H
+#define VCARD_EMUL_H 1
+
+#include "card_7816t.h"
+#include "vcard.h"
+#include "vcard_emul_type.h"
+
+/*
+ * types
+ */
+typedef enum {
+    VCARD_EMUL_OK =0,
+    VCARD_EMUL_FAIL
+} VCardEmulError;
+
+/* options are emul specific. call card_emul_parse_args to change a string
+ * To an options struct */
+typedef struct VCardEmulOptionsStruct VCardEmulOptions;
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int vcard_emul_get_login_count(VCard *card);
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t vcard_emul_login(VCard *card, unsigned char *pin,
+                                     int pin_len);
+
+/*
+ * key functions
+ */
+/* delete a key */
+void vcard_emul_delete_key(VCardKey *key);
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                                  unsigned char *buffer, int buffer_size);
+
+void vcard_emul_reset(VCard *card, VCardPower power);
+void vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len);
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError vcard_emul_force_card_insert(VReader *vreader);
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError vcard_emul_force_card_remove(VReader *vreader);
+
+VCardEmulOptions *vcard_emul_options(const char *args);
+VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
+void vcard_emul_usage(void);
+#endif
diff --git a/libcaccard/vcard_emul_nss.c b/libcaccard/vcard_emul_nss.c
new file mode 100644
index 0000000..5b7f9a2
--- /dev/null
+++ b/libcaccard/vcard_emul_nss.c
@@ -0,0 +1,1147 @@
+/*
+ * This is the actual card emulator.
+ *
+ * These functions can be implemented in different ways on different platforms
+ * using the underlying system primitives. For Linux it uses NSS, though direct
+ * to PKCS #11, openssl+pkcs11, or even gnu crypto libraries+pkcs #11 could be
+ * used. On Windows CAPI could be used.
+ */
+#include "vcard.h"
+#include "card_7816t.h"
+#include "vcard_emul.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * NSS headers
+ */
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.h>
+#include <key.h>
+#include <secmod.h>
+#include <prthread.h>
+#include <secerr.h>
+
+/*
+ * system headers
+ */
+#include <stdlib.h>
+#include <string.h>
+
+
+struct VCardKeyStruct {
+    CERTCertificate *cert;
+    PK11SlotInfo *slot;
+    SECKEYPrivateKey *key;
+};
+
+
+typedef struct VirtualReaderOptionsStruct VirtualReaderOptions;
+
+struct VReaderEmulStruct {
+    PK11SlotInfo *slot;
+    VCardEmulType default_type;
+    char *type_params;
+    PRBool present;
+    int     series;
+    VCard *saved_vcard;
+};
+
+/*
+ *  NSS Specific options
+ */
+struct VirtualReaderOptionsStruct {
+    char *name;
+    char *vname;
+    VCardEmulType card_type;
+    char *type_params;
+    char **cert_name;
+    int cert_count;
+};
+
+struct VCardEmulOptionsStruct {
+    void *nss_db;
+    VirtualReaderOptions *vreader;
+    int vreader_count;
+    VCardEmulType hw_card_type;
+    const char *hw_type_params;
+    PRBool use_hw;
+};
+
+static int nss_emul_init = 0;
+
+/* if we have more that just the slot, define
+ * VCardEmulStruct here */
+
+/*
+ * allocate the set of arrays for certs, cert_len, key
+ */
+static PRBool
+vcard_emul_alloc_arrays(unsigned char ***certsp, int **cert_lenp,
+                        VCardKey ***keysp, int cert_count)
+{
+    *certsp = NULL;
+    *cert_lenp = NULL;
+    *keysp = NULL;
+    *certsp = (unsigned char **)malloc(sizeof(unsigned char *)*cert_count);
+    if (*certsp == NULL) {
+        return PR_FALSE;
+    }
+    *cert_lenp = (int *)malloc(sizeof(int)*cert_count);
+    if (*cert_lenp == NULL) {
+        free(*certsp);
+        *certsp = NULL;
+        return PR_FALSE;
+    }
+    *keysp = (VCardKey **)malloc(sizeof(VCardKey *)*cert_count);
+    if (*keysp != NULL) {
+        return PR_TRUE;
+    }
+    free(*cert_lenp);
+    free(*certsp);
+    *cert_lenp = NULL;
+    *certsp = NULL;
+    return PR_FALSE;
+}
+
+/*
+ * Emulator specific card information
+ */
+typedef struct CardEmulCardStruct CardEmulPrivate;
+
+static VCardEmul *
+vcard_emul_new_card(PK11SlotInfo *slot)
+{
+    PK11_ReferenceSlot(slot);
+    /* currently we don't need anything other than the slot */
+    return (VCardEmul *)slot;
+}
+
+static void
+vcard_emul_delete_card(VCardEmul *vcard_emul)
+{
+    PK11SlotInfo *slot = (PK11SlotInfo *)vcard_emul;
+    if (slot == NULL) {
+        return;
+    }
+    PK11_FreeSlot(slot);
+}
+
+static PK11SlotInfo *
+vcard_emul_card_get_slot(VCard *card)
+{
+    /* note, the card is holding the reference, no need to get another one */
+    return (PK11SlotInfo *)vcard_get_private(card);
+}
+
+
+/*
+ * key functions
+ */
+/* private constructure */
+static VCardKey *
+vcard_emul_make_key(PK11SlotInfo *slot, CERTCertificate *cert)
+{
+    VCardKey * key;
+
+    key = (VCardKey *)malloc(sizeof(VCardKey));
+    if (key == NULL) {
+        return NULL;
+    }
+    key->slot = PK11_ReferenceSlot(slot);
+    key->cert = CERT_DupCertificate(cert);
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    /* NOTE: the cert is a temp cert, not necessarily the cert in the token,
+     * use the DER version of this function */
+    key->key = PK11_FindKeyByDERCert(slot,cert, NULL);
+    return key;
+}
+
+/* destructor */
+void
+vcard_emul_delete_key(VCardKey *key)
+{
+    if (!nss_emul_init || (key == NULL)) {
+        return;
+    }
+    if (key->key) {
+        SECKEY_DestroyPrivateKey(key->key);
+        key->key = NULL;
+    }
+    if (key->cert) {
+        CERT_DestroyCertificate(key->cert);
+    }
+    if (key->slot) {
+        PK11_FreeSlot(key->slot);
+    }
+    return;
+}
+
+/*
+ * grab the nss key from a VCardKey. If it doesn't exist, try to look it up
+ */
+static SECKEYPrivateKey *
+vcard_emul_get_nss_key(VCardKey *key)
+{
+    if (key->key) {
+        return key->key;
+    }
+    /* NOTE: if we aren't logged into the token, this could return NULL */
+    key->key = PK11_FindPrivateKeyFromCert(key->slot,key->cert, NULL);
+    return key->key;
+}
+
+/*
+ * Map NSS errors to 7816 errors
+ */
+static vcard_7816_status_t
+vcard_emul_map_error(int error)
+{
+    switch (error) {
+    case SEC_ERROR_TOKEN_NOT_LOGGED_IN:
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    case SEC_ERROR_BAD_DATA:
+    case SEC_ERROR_OUTPUT_LEN:
+    case SEC_ERROR_INPUT_LEN:
+    case SEC_ERROR_INVALID_ARGS:
+    case SEC_ERROR_INVALID_ALGORITHM:
+    case SEC_ERROR_NO_KEY:
+    case SEC_ERROR_INVALID_KEY:
+    case SEC_ERROR_DECRYPTION_DISALLOWED:
+        return VCARD7816_STATUS_ERROR_DATA_INVALID;
+    case SEC_ERROR_NO_MEMORY:
+        return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    }
+    return VCARD7816_STATUS_EXC_ERROR_CHANGE;
+}
+
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+vcard_7816_status_t
+vcard_emul_rsa_op(VCard *card, VCardKey *key,
+                  unsigned char *buffer, int buffer_size)
+{
+    SECKEYPrivateKey *priv_key;
+    unsigned signature_len;
+    SECStatus rv;
+
+    if ((!nss_emul_init) || (key == NULL)) {
+        /* couldn't get the key, indicate that we aren't logged in */
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    priv_key = vcard_emul_get_nss_key(key);
+
+    /*
+     * this is only true of the rsa signature
+     */
+    signature_len = PK11_SignatureLen(priv_key);
+    if (buffer_size != signature_len) {
+        return  VCARD7816_STATUS_ERROR_DATA_INVALID;
+    }
+    rv = PK11_PrivDecryptRaw(priv_key, buffer, &signature_len, signature_len,
+                             buffer, buffer_size);
+    if (rv != SECSuccess) {
+        return vcard_emul_map_error(PORT_GetError());
+    }
+    ASSERT(buffer_size == signature_len);
+    return VCARD7816_STATUS_SUCCESS;
+}
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown,
+ * return -1 */
+int
+vcard_emul_get_login_count(VCard *card)
+{
+    return -1;
+}
+
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+vcard_7816_status_t
+vcard_emul_login(VCard *card, unsigned char *pin, int pin_len)
+{
+    PK11SlotInfo *slot;
+    unsigned char *pin_string = NULL;
+    int i;
+    SECStatus rv;
+
+    if (!nss_emul_init) {
+        return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+    }
+    slot = vcard_emul_card_get_slot(card);
+     /* We depend on the PKCS #11 module internal login state here because we
+      * create a separate process to handle each guest instance. If we needed
+      * to handle multiple guests from one process, then we would need to keep
+      * a lot of extra state in our card structure
+      * */
+    pin_string = malloc(pin_len+1);
+    if (pin_string == NULL) {
+        return VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE;
+    }
+    memcpy(pin_string,pin,pin_len);
+    pin_string[pin_len] = 0;
+
+    /* handle CAC expanded pins correctly */
+    for (i = pin_len-1; i >= 0 && (pin_string[i] == 0xff); i--) {
+        pin_string[i] = 0;
+    }
+
+    rv = PK11_Authenticate(slot, PR_FALSE, pin_string);
+    memset(pin_string, 0, pin_len);  /* don't let the pin hang around in memory
+                                        to be snooped */
+    free(pin_string);
+    if (rv == SECSuccess) {
+        return VCARD7816_STATUS_SUCCESS;
+    }
+    /* map the error from port get error */
+    return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+}
+
+void
+vcard_emul_reset(VCard *card, VCardPower power)
+{
+    PK11SlotInfo *slot;
+
+    if (!nss_emul_init) {
+        return;
+    }
+
+    /* if we reset the card (either power on or power off), we loose our login
+     * state */
+    /* TODO: we may also need to send insertion/removal events? */
+    slot = vcard_emul_card_get_slot(card);
+    (void)PK11_Logout(slot);
+    return;
+}
+
+
+static VReader *
+vcard_emul_find_vreader_from_slot(PK11SlotInfo *slot)
+{
+    VReaderList *reader_list = vreader_get_reader_list();
+    VReaderListEntry *current_entry = NULL;
+
+    if (reader_list == NULL) {
+        return NULL;
+    }
+    for (current_entry= vreader_list_get_first(reader_list); current_entry;
+                        current_entry=vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderEmul *reader_emul = vreader_get_private(reader);
+        if (reader_emul->slot == slot) {
+            return reader;
+        }
+        vreader_free(reader);
+    }
+
+    return NULL;
+}
+
+/*
+ * create a new reader emul
+ */
+static VReaderEmul *
+vreader_emul_new(PK11SlotInfo *slot, VCardEmulType type, const char *params)
+{
+    VReaderEmul *new_reader_emul;
+
+    new_reader_emul = (VReaderEmul *)malloc(sizeof(VReaderEmul));
+    if (new_reader_emul == NULL) {
+        return NULL;
+    }
+
+    new_reader_emul->slot = PK11_ReferenceSlot(slot);
+    new_reader_emul->default_type = type;
+    new_reader_emul->type_params = strdup(params);
+    new_reader_emul->present = PR_FALSE;
+    new_reader_emul->series = 0;
+    new_reader_emul->saved_vcard = NULL;
+    return new_reader_emul;
+}
+
+static void
+vreader_emul_delete(VReaderEmul *vreader_emul)
+{
+    if (vreader_emul == NULL) {
+        return;
+    }
+    if (vreader_emul->slot) {
+        PK11_FreeSlot(vreader_emul->slot);
+    }
+    if (vreader_emul->type_params) {
+        free(vreader_emul->type_params);
+    }
+    free(vreader_emul);
+}
+
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static VCardEmulType
+vcard_emul_get_type(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->default_type != VCARD_EMUL_NONE) {
+        return vreader_emul->default_type;
+    }
+
+    return vcard_emul_type_select(vreader);
+}
+/*
+ *  TODO: move this to emulater non-specific file
+ */
+static const char *
+vcard_emul_get_type_params(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+
+    vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul && vreader_emul->type_params) {
+        return vreader_emul->type_params;
+    }
+
+    return "";
+}
+
+/* pull the slot out of the reader private data */
+static PK11SlotInfo *
+vcard_emul_reader_get_slot(VReader *vreader)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    if (vreader_emul == NULL) {
+        return NULL;
+    }
+    return vreader_emul->slot;
+}
+
+/*
+ *  Card ATR's map to physical cards. VCARD_ATR_PREFIX will set appropriate
+ *  historical bytes for any software emulated card. The remaining bytes can be
+ *  used to indicate the actual emulator
+ */
+static const unsigned char nss_atr[] = { VCARD_ATR_PREFIX(3), 'N', 'S', 'S' };
+
+void
+vcard_emul_get_atr(VCard *card, unsigned char *atr, int *atr_len)
+{
+    int len = MIN(sizeof(nss_atr), *atr_len);
+    ASSERT(atr != NULL);
+
+    memcpy(atr, nss_atr, len);
+    *atr_len = len;
+    return;
+}
+
+/*
+ * create a new card from certs and keys
+ */
+static VCard *
+vcard_emul_make_card(VReader *reader,
+                     unsigned char * const *certs, int *cert_len,
+                     VCardKey *keys[], int cert_count)
+{
+    VCardEmul *vcard_emul;
+    VCard *vcard;
+    PK11SlotInfo *slot;
+    VCardEmulType type;
+    const char *params;
+
+    type = vcard_emul_get_type(reader);
+
+    /* ignore the inserted card */
+    if (type == VCARD_EMUL_NONE) {
+        return NULL;
+    }
+    slot = vcard_emul_reader_get_slot(reader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    params = vcard_emul_get_type_params(reader);
+    /* params these can be NULL */
+
+    vcard_emul = vcard_emul_new_card(slot);
+    if (vcard_emul == NULL) {
+        return NULL;
+    }
+    vcard = vcard_new(vcard_emul, vcard_emul_delete_card);
+    if (vcard == NULL) {
+        vcard_emul_delete_card(vcard_emul);
+        return NULL;
+    }
+    vcard_init(reader, vcard, type, params, certs, cert_len, keys, cert_count);
+    return vcard;
+}
+
+
+/*
+ * 'clone' a physical card as a virtual card
+ */
+static VCard *
+vcard_emul_mirror_card(VReader *vreader)
+{
+    /*
+     * lookup certs using the C_FindObjects. The Stan Cert handle won't give
+     * us the real certs until we log in.
+     */
+    PK11GenericObject *firstObj, *thisObj;
+    int cert_count;
+    unsigned char **certs;
+    int *cert_len;
+    VCardKey **keys;
+    PK11SlotInfo *slot;
+    PRBool ret;
+
+    slot = vcard_emul_reader_get_slot(vreader);
+    if (slot == NULL) {
+        return NULL;
+    }
+
+    firstObj = PK11_FindGenericObjects(slot, CKO_CERTIFICATE);
+    if (firstObj == NULL) {
+        return NULL;
+    }
+
+    /* count the certs */
+    cert_count=0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        cert_count++;
+    }
+
+    if (cert_count == 0) {
+        PK11_DestroyGenericObjects(firstObj);
+        return NULL;
+    }
+
+    /* allocate the arrays */
+    ret = vcard_emul_alloc_arrays(&certs,&cert_len, &keys, cert_count);
+    if (ret == PR_FALSE) {
+        return NULL;
+    }
+
+    /* fill in the arrays */
+    cert_count = 0;
+    for (thisObj = firstObj; thisObj;
+                             thisObj = PK11_GetNextGenericObject(thisObj)) {
+        SECItem derCert;
+        CERTCertificate *cert;
+        SECStatus rv;
+
+        rv = PK11_ReadRawAttribute(PK11_TypeGeneric, thisObj,
+                                   CKA_VALUE, &derCert);
+        if (rv != SECSuccess) {
+            continue;
+        }
+        /* create floating temp cert. This gives us a cert structure even if
+         * the token isn't logged in */
+        cert = CERT_NewTempCertificate(CERT_GetDefaultCertDB(), &derCert,
+                                       NULL, PR_FALSE, PR_TRUE);
+        SECITEM_FreeItem(&derCert, PR_FALSE);
+        if (cert == NULL) {
+            continue;
+        }
+
+        certs[cert_count] = cert->derCert.data;
+        cert_len[cert_count] = cert->derCert.len;
+        keys[cert_count] = vcard_emul_make_key(slot, cert);
+        cert_count++;
+        CERT_DestroyCertificate(cert); /* key obj still has a reference */
+    }
+
+    /* now create the card */
+    return vcard_emul_make_card(vreader, certs, cert_len, keys, cert_count);
+}
+
+static VCardEmulType default_card_type = VCARD_EMUL_NONE;
+static const char *default_type_params = "";
+
+/*
+ * This thread looks for card and reader insertions and puts events on the
+ * event queue
+ */
+static void
+vcard_emul_event_thread(void *arg)
+{
+    PK11SlotInfo *slot;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+    SECMODModule *module = (SECMODModule *)arg;
+
+    do {
+        slot = SECMOD_WaitForAnyTokenEvent(module, 0, 500);
+        if (slot == NULL) {
+            break;
+        }
+        vreader = vcard_emul_find_vreader_from_slot(slot);
+        if (vreader == NULL) {
+            /* new vreader */
+            vreader_emul = vreader_emul_new(slot, default_card_type,
+                                            default_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            PK11_FreeSlot(slot);
+            slot = NULL;
+            vreader_add_reader(vreader);
+            vreader_free(vreader);
+            continue;
+        }
+        /* card remove/insert */
+        vreader_emul = vreader_get_private(vreader);
+        if (PK11_IsPresent(slot)) {
+            int series = PK11_GetSlotSeries(slot);
+            if (series != vreader_emul->series) {
+                if (vreader_emul->present) {
+                    vreader_insert_card(vreader, NULL);
+                }
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_free(vcard);
+            }
+            vreader_emul->series = series;
+            vreader_emul->present = 1;
+            vreader_free(vreader);
+            PK11_FreeSlot(slot);
+            continue;
+        }
+        if (vreader_emul->present) {
+            vreader_insert_card(vreader, NULL);
+        }
+        vreader_emul->series = 0;
+        vreader_emul->present = 0;
+        PK11_FreeSlot(slot);
+        vreader_free(vreader);
+    } while(1);
+}
+
+/* if the card is inserted when we start up, make sure our state is correct */
+static void
+vcard_emul_init_series(VReader *vreader, VCard *vcard)
+{
+    VReaderEmul *vreader_emul = vreader_get_private(vreader);
+    PK11SlotInfo *slot = vreader_emul->slot;
+
+    vreader_emul->present = PK11_IsPresent(slot);
+    vreader_emul->series = PK11_GetSlotSeries(slot);
+    if (vreader_emul->present == 0) {
+        vreader_insert_card(vreader, NULL);
+    }
+}
+
+/*
+ * each module has a separate wait call, create a thread for each module that
+ * we are using.
+ */
+static void
+vcard_emul_new_event_thread(SECMODModule *module)
+{
+     PR_CreateThread(PR_SYSTEM_THREAD, vcard_emul_event_thread,
+                     module, PR_PRIORITY_HIGH, PR_GLOBAL_THREAD,
+                     PR_UNJOINABLE_THREAD, 0);
+}
+
+static const VCardEmulOptions default_options = {
+    .nss_db = NULL,
+    .vreader = NULL,
+    .vreader_count = 0,
+    .hw_card_type = VCARD_EMUL_CAC,
+    .hw_type_params = "",
+    .use_hw = PR_TRUE
+};
+
+
+/*
+ *  NSS needs the app to supply a password prompt. In our case the only time
+ *  the password is supplied is as part of the Login APDU. The actual password
+ *  is passed in the pw_arg in that case. In all other cases pw_arg should be
+ *  NULL.
+ */
+static char *
+vcard_emul_get_password(PK11SlotInfo *slot, PRBool retries, void *pw_arg)
+{
+    /* if it didn't work the first time, don't keep trying */
+    if (retries) {
+        return NULL;
+    }
+    /* we are looking up a password when we don't have one in hand */
+    if (pw_arg == NULL) {
+        return NULL;
+    }
+    /* TODO: we really should verify that were are using the right slot */
+    return PORT_Strdup(pw_arg);
+}
+
+/* Force a card removal even if the card is not physically removed */
+VCardEmulError
+vcard_emul_force_card_remove(VReader *vreader)
+{
+    if (!nss_emul_init || (vreader_card_is_present(vreader) != VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+
+    /* OK, remove it */
+    vreader_insert_card(vreader, NULL);
+    return VCARD_EMUL_OK;
+}
+
+/* Re-insert of a card that has been removed by force removal */
+VCardEmulError
+vcard_emul_force_card_insert(VReader *vreader)
+{
+    VReaderEmul *vreader_emul;
+    VCard *vcard;
+
+    if (!nss_emul_init || (vreader_card_is_present(vreader) == VREADER_OK)) {
+        return VCARD_EMUL_FAIL; /* card is already removed */
+    }
+    vreader_emul = vreader_get_private(vreader);
+
+    /* if it's a softcard, get the saved vcard from the reader emul structure */
+    if (vreader_emul->saved_vcard) {
+        vcard = vcard_reference(vreader_emul->saved_vcard);
+    } else {
+        /* it must be a physical card, rebuild it */
+        if (!PK11_IsPresent(vreader_emul->slot)) {
+            /* physical card has been removed, not way to reinsert it */
+            return VCARD_EMUL_FAIL;
+        }
+        vcard = vcard_emul_mirror_card(vreader);
+    }
+    vreader_insert_card(vreader, vcard);
+    vcard_free(vcard);
+
+    return VCARD_EMUL_OK;
+}
+
+/* Previously we returned FAIL if no readers found. This makes
+ * no sense when using hardware, since there may be no readers connected
+ * at the time vcard_emul_init is called, but they will be properly
+ * recognized later. So Instead return FAIL only if no_hw==1 and no
+ * vcards can be created (indicates error with certificates provided
+ * or db), or if any other higher level error (NSS error, missing coolkey). */
+VCardEmulError
+vcard_emul_init(const VCardEmulOptions *options)
+{
+    SECStatus rv;
+    PRBool ret, has_readers=PR_FALSE, need_module;
+    VReader *vreader;
+    VReaderEmul *vreader_emul;
+    SECMODListLock *module_lock;
+    SECMODModuleList *module_list;
+    SECMODModuleList *mlp;
+    int i;
+
+    vreader_init();
+    vevent_queue_init();
+
+    if (options == NULL) {
+        options = &default_options;
+    }
+
+    /* first initialize NSS */
+    if (options->nss_db) {
+        rv = NSS_Init(options->nss_db);
+    } else {
+        rv = NSS_Init("sql:/etc/pki/nssdb");
+    }
+    if (rv != SECSuccess) {
+        return VCARD_EMUL_FAIL;
+    }
+    /* Set password callback function */
+    PK11_SetPasswordFunc(vcard_emul_get_password);
+
+    /* set up soft cards emulated by software certs rather than physical cards
+     * */
+    for (i = 0; i < options->vreader_count; i++) {
+        int j;
+        int cert_count;
+        unsigned char **certs;
+        int *cert_len;
+        VCardKey **keys;
+        PK11SlotInfo *slot;
+
+        slot = PK11_FindSlotByName(options->vreader[i].name);
+        if (slot == NULL) {
+            continue;
+        }
+        vreader_emul = vreader_emul_new(slot, options->vreader[i].card_type,
+                                        options->vreader[i].type_params);
+        vreader = vreader_new(options->vreader[i].vname, vreader_emul,
+                              vreader_emul_delete);
+        vreader_add_reader(vreader);
+        cert_count = options->vreader[i].cert_count;
+
+        ret = vcard_emul_alloc_arrays(&certs, &cert_len, &keys,
+                                      options->vreader[i].cert_count);
+        if (ret == PR_FALSE) {
+            continue;
+        }
+        cert_count = 0;
+        for (j=0; j < options->vreader[i].cert_count; j++) {
+            /* we should have a better way of identifying certs than by
+             * nickname here */
+            CERTCertificate *cert = PK11_FindCertFromNickname(
+                                        options->vreader[i].cert_name[j],
+                                        NULL);
+            if (cert == NULL) {
+                continue;
+            }
+            certs[cert_count] = cert->derCert.data;
+            cert_len[cert_count] = cert->derCert.len;
+            keys[cert_count] = vcard_emul_make_key(slot, cert);
+            /* this is safe because the key is still holding a cert reference */
+            CERT_DestroyCertificate(cert);
+            cert_count++;
+        }
+        if (cert_count) {
+            VCard *vcard = vcard_emul_make_card(vreader, certs, cert_len,
+                                                keys, cert_count);
+            vreader_insert_card(vreader, vcard);
+            vcard_emul_init_series(vreader, vcard);
+            /* allow insertion and removal of soft cards */
+            vreader_emul->saved_vcard = vcard_reference(vcard);
+            vcard_free(vcard);
+            vreader_free(vreader);
+            has_readers = PR_TRUE;
+        }
+    }
+
+    /* if we aren't suppose to use hw, skip looking up hardware tokens */
+    if (!options->use_hw) {
+        nss_emul_init = has_readers;
+        return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL;
+    }
+
+    /* make sure we have some PKCS #11 module loaded */
+    module_lock = SECMOD_GetDefaultModuleListLock();
+    module_list = SECMOD_GetDefaultModuleList();
+    need_module = !has_readers;
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule * module = mlp->module;
+        if (SECMOD_HasRemovableSlots(module)) {
+            need_module = PR_FALSE;
+            break;
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+
+    if (need_module) {
+        SECMODModule *module;
+        module = SECMOD_LoadUserModule(
+                    (char*)"library=libcoolkeypk11.so name=Coolkey",
+                    NULL, PR_FALSE);
+        if (module == NULL) {
+            return VCARD_EMUL_FAIL;
+        }
+        SECMOD_DestroyModule(module); /* free our reference, Module will still
+                                       * be on the list.
+                                       * until we destroy it */
+    }
+
+    /* now examine all the slots, finding which should be readers */
+    /* We should control this with options. For now we mirror out any
+     * removable hardware slot */
+    default_card_type = options->hw_card_type;
+    default_type_params = strdup(options->hw_type_params);
+
+    SECMOD_GetReadLock(module_lock);
+    for (mlp = module_list; mlp; mlp = mlp->next) {
+        SECMODModule * module = mlp->module;
+        PRBool has_emul_slots = PR_FALSE;
+
+        if (module == NULL) {
+                continue;
+        }
+
+        for (i=0; i < module->slotCount; i++) {
+            PK11SlotInfo *slot = module->slots[i];
+
+            /* only map removable HW slots */
+            if (slot == NULL || !PK11_IsRemovable(slot) || !PK11_IsHW(slot)) {
+                continue;
+            }
+            vreader_emul = vreader_emul_new(slot, options->hw_card_type,
+                                            options->hw_type_params);
+            vreader = vreader_new(PK11_GetSlotName(slot), vreader_emul,
+                                  vreader_emul_delete);
+            vreader_add_reader(vreader);
+
+            has_readers = PR_TRUE;
+            has_emul_slots = PR_TRUE;
+
+            if (PK11_IsPresent(slot)) {
+                VCardEmulType type;
+                VCard *vcard;
+                type = vcard_emul_get_type(vreader);
+                vcard = vcard_emul_mirror_card(vreader);
+                vreader_insert_card(vreader, vcard);
+                vcard_emul_init_series(vreader, vcard);
+                vcard_free(vcard);
+            }
+        }
+        if (has_emul_slots) {
+            vcard_emul_new_event_thread(module);
+        }
+    }
+    SECMOD_ReleaseReadLock(module_lock);
+    nss_emul_init = has_readers;
+
+    return VCARD_EMUL_OK;
+}
+
+/*
+ *  Silly little functions to help parsing our argument string
+ */
+static char *
+copy_string(const char *str, int str_len)
+{
+    char *new_str;
+
+    new_str = malloc(str_len+1);
+    memcpy(new_str, str, str_len);
+    new_str[str_len] = 0;
+    return new_str;
+}
+
+static int
+count_tokens(const char *str, char token, char token_end)
+{
+    int count = 0;
+
+    for (;*str;str++) {
+        if (*str == token) {
+            count++;
+        }
+        if (*str == token_end) {
+            break;
+        }
+    }
+    return count;
+}
+
+static const char *
+find_token(const char *str, char token, char token_end)
+{
+    /* just do the blind simple thing */
+    for (;*str;str++) {
+        if ((*str == token) || (*str == token_end)) {
+            break;
+        }
+    }
+    return str;
+}
+
+static const char *
+strip(const char *str)
+{
+    for(;*str; str++) {
+        if ((*str != ' ') && (*str != '\n') &&
+           (*str != '\t') && (*str != '\r')) {
+            break;
+        }
+    }
+    return str;
+}
+
+static const char *
+find_blank(const char *str)
+{
+    for(;*str; str++) {
+        if ((*str == ' ') || (*str == '\n') ||
+           (*str == '\t') || (*str == '\r')) {
+            break;
+        }
+    }
+    return str;
+}
+
+
+/*
+ *  We really want to use some existing argument parsing library here. That
+ *  would give us a consistant look */
+static VCardEmulOptions options;
+#define READER_STEP 4
+
+VCardEmulOptions *
+vcard_emul_options(const char *args)
+{
+    int reader_count = 0;
+    VCardEmulOptions *opts;
+    char type_str[100];
+    int type_len;
+
+    /* Allow the future use of allocating the options structure on the fly */
+    memcpy(&options, &default_options, sizeof(options));
+    opts = &options;
+
+    do {
+        args = strip(args); /* strip off the leading spaces */
+        if (*args == ',') {
+           continue;
+        }
+        /* soft=(slot_name,virt_name,emul_type,emul_flags,cert_1, (no eol)
+         *       cert_2,cert_3...) */
+        if (strncmp(args,"soft=",5) == 0) {
+            const char *name;
+            const char *vname;
+            const char *type_params;
+            VCardEmulType type;
+            int name_length, vname_length, type_params_length, count, i;
+            VirtualReaderOptions *vreaderOpt = NULL;
+
+            args = strip(args+5);
+            if (*args != '(') {
+                continue;
+            }
+            name = args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args+1);
+            name_length = args - name - 2;
+            vname = args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            vname_length = args - name - 2;
+            args = strip(args+1);
+            type_len = find_token(args,',',')') - args;
+            assert(sizeof(type_str) > type_len);
+            strncpy(type_str, args, type_len);
+            type_str[type_len] = 0;
+            type = vcard_emul_type_from_string(type_str);
+            args = find_token(args,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            args = strip(args++);
+            type_params=args;
+            args = find_token(args+1,',',')');
+            if (*args == 0) {
+                break;
+            }
+            if (*args == ')') {
+                args++;
+                continue;
+            }
+            type_params_length = args - name;
+            args = strip(args++);
+            if (*args == 0) {
+                break;
+            }
+
+            if (opts->vreader_count >= reader_count) {
+                reader_count += READER_STEP;
+                vreaderOpt = realloc(opts->vreader,
+                                reader_count*sizeof(*vreaderOpt));
+                if (vreaderOpt == NULL) {
+                    return opts; /* we're done */
+                }
+            }
+            opts->vreader = vreaderOpt;
+            vreaderOpt = &vreaderOpt[opts->vreader_count];
+            vreaderOpt->name = copy_string(name, name_length);
+            vreaderOpt->vname = copy_string(vname, vname_length);
+            vreaderOpt->card_type = type;
+            vreaderOpt->type_params = copy_string(name, name_length);
+            count = count_tokens(args,',',')');
+            vreaderOpt->cert_count = count;
+            vreaderOpt->cert_name = (char **)malloc(count*sizeof(char *));
+            for (i=0; i < count; i++) {
+                const char *cert = args + 1;
+                args = find_token(args + 1, ',', ')');
+                vreaderOpt->cert_name[i] = copy_string(cert, args - cert);
+            }
+            if (*args == ')') {
+                args++;
+            }
+            opts->vreader_count++;
+        /* use_hw= */
+        } else if (strncmp(args,"use_hw=",7) == 0) {
+            args = strip(args+7);
+            if (*args == '0' || *args == 'N' || *args == 'n' || *args == 'F') {
+                opts->use_hw = PR_FALSE;
+            } else {
+                opts->use_hw = PR_TRUE;
+            }
+            args = find_blank(args);
+        /* hw_type= */
+        } else if (strncmp(args,"hw_type=",8) == 0) {
+            args = strip(args+8);
+            opts->hw_card_type = vcard_emul_type_from_string(args);
+            args = find_blank(args);
+        /* hw_params= */
+        } else if (strncmp(args,"hw_params=",10) == 0) {
+            const char *params;
+            args = strip(args+10);
+            params= args;
+            args = find_blank(args);
+            opts->hw_type_params = copy_string(params, args-params);
+        /* db="/data/base/path" */
+        } else if (strncmp(args,"db=",3) == 0) {
+            const char *db;
+            args = strip(args+3);
+            if (*args != '"') {
+                continue;
+            }
+            args++;
+            db = args;
+            args = find_token(args, '"', '\n');
+            opts->nss_db = copy_string(db,args-db);
+            if (*args != 0) {
+                args++;
+            }
+        } else args = find_blank(args);
+    } while (*args != 0);
+
+    return opts;
+}
+
+void
+vcard_emul_usage(void)
+{
+   fprintf(stderr,
+"emul args: comma separated list of the following arguments\n"
+" db={nss_database}               (default sql:/etc/pki/nssdb)\n"
+" use_hw=[yes|no]                 (default yes)\n"
+" hw_type={card_type_to_emulate}  (default CAC)\n"
+" hw_param={param_for_card}       (default \"\")\n"
+" soft=({slot_name},{vreader_name},{card_type_to_emulate},{params_for_card},\n"
+"       {cert1},{cert2},{cert3}    (default none)\n"
+"\n"
+"  {nss_database}          The location of the NSS cert & key database\n"
+"  {card_type_to_emulate}  What card interface to present to the guest\n"
+"  {param_for_card}        Card interface specific parameters\n"
+"  {slot_name}             NSS slot that contains the certs\n"
+"  {vreader_name}          Virutal reader name to present to the guest\n"
+"  {certN}                 Nickname of the certificate n on the virtual card\n"
+"\n"
+"These parameters come as a single string separated by blanks or newlines."
+"\n"
+"Unless use_hw is set to no, all tokens that look like removable hardware\n"
+"tokens will be presented to the guest using the emulator specified by \n"
+"hw_type, and parameters of hw_param.\n"
+"\n"
+"If more one or more soft= parameters are specified, these readers will be\n"
+"presented to the guest\n");
+}
diff --git a/libcaccard/vcard_emul_type.c b/libcaccard/vcard_emul_type.c
new file mode 100644
index 0000000..adbc54b
--- /dev/null
+++ b/libcaccard/vcard_emul_type.c
@@ -0,0 +1,60 @@
+/*
+ *  This file contains utility functions which abstract the different card
+ *  types.  The goal is that new card types can easily be added by simply
+ *  changing this file and vcard_emul_type.h. It is currently not a requirement
+ *  to dynamically add new card types.
+ */
+
+#include <strings.h>
+#include "vcardt.h"
+#include "vcard_emul_type.h"
+#include "cac.h"
+#include "passthru.h"
+
+VCardStatus vcard_init(VReader *vreader, VCard *vcard,
+                       VCardEmulType type, const char * params,
+                       unsigned char *const *cert, int cert_len[],
+                       VCardKey *key[], int cert_count)
+{
+    switch (type) {
+    case VCARD_EMUL_NONE:
+        break;
+    case VCARD_EMUL_CAC:
+        return cac_card_init(vreader, vcard, params,
+                             cert, cert_len, key,  cert_count);
+#ifdef USE_PASSTHRU
+    case VCARD_EMUL_PASSTHRU:
+        return passthru_card_init(vreader, vcard, params,
+                                  cert, cert_len, key,  cert_count);
+#endif
+    /* add new ones here */
+    default:
+        break;
+    }
+    return VCARD_FAIL;
+}
+
+VCardEmulType vcard_emul_type_select(VReader *vreader)
+{
+#ifdef notdef
+    /* since there is only one emulator no need to call this function */
+    if (cac_is_cac_card(vreader) == VCARD_DONE) {
+        return VCARD_EMUL_CAC;
+    }
+#endif
+    /* return the default */
+    return VCARD_EMUL_CAC;
+}
+
+VCardEmulType vcard_emul_type_from_string(const char *type_string)
+{
+     if (strcasecmp(type_string,"CAC") == 0) {
+        return VCARD_EMUL_CAC;
+     }
+#ifdef USE_PASSTHRU
+     if (strcasecmp(type_string,"PASSTHRU") == 0) {
+        return VCARD_EMUL_PASSTHRU;
+     }
+#endif
+     return VCARD_EMUL_NONE;
+}
diff --git a/libcaccard/vcard_emul_type.h b/libcaccard/vcard_emul_type.h
new file mode 100644
index 0000000..da15528
--- /dev/null
+++ b/libcaccard/vcard_emul_type.h
@@ -0,0 +1,29 @@
+/*
+ *  This header file abstracts the different card types. The goal is new card
+ *  types can easily be added by simply changing this file and
+ *  vcard_emul_type.c. It is currently not a requirement to dynamically add new
+ *  card types.
+ */
+
+#ifndef VCARD_EMUL_TYPE_H
+#define VCARD_EMUL_TYPE_H 1
+#include "vcardt.h"
+#include "vreadert.h"
+
+/*
+ * types
+ */
+typedef enum {
+     VCARD_EMUL_NONE =0,
+     VCARD_EMUL_CAC,
+     VCARD_EMUL_PASSTHRU
+} VCardEmulType;
+
+/* functions used by the rest of the emulator */
+VCardStatus vcard_init(VReader *vreader, VCard *vcard, VCardEmulType type,
+                       const char *params, unsigned char * const *cert,
+                       int cert_len[], VCardKey *key[], int cert_count);
+VCardEmulType vcard_emul_type_select(VReader *vreader);
+VCardEmulType vcard_emul_type_from_string(const char *type_string);
+
+#endif
diff --git a/libcaccard/vcardt.h b/libcaccard/vcardt.h
new file mode 100644
index 0000000..e371451
--- /dev/null
+++ b/libcaccard/vcardt.h
@@ -0,0 +1,66 @@
+/*
+ *
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#ifndef ASSERT
+#define ASSERT assert
+#endif
+#ifndef MIN
+#define MIN(x,y) ((x)>(y)?(y):(x))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+#endif
+
+typedef struct VCardStruct VCard;
+typedef struct VCardAPDUStruct VCardAPDU;
+typedef struct VCardResponseStruct VCardResponse;
+typedef struct VCardBufferResponseStruct VCardBufferResponse;
+typedef struct VCardAppletStruct VCardApplet;
+typedef struct VCardAppletPrivateStruct VCardAppletPrivate;
+typedef struct VCardKeyStruct VCardKey;  /* opaque */
+typedef struct VCardEmulStruct VCardEmul;
+
+#define MAX_CHANNEL 4
+
+/* create an ATR with appropriate historical bytes */
+#define VCARD_ATR_PREFIX(size) 0x3b, 0x66+(size), 0x00, 0xff, \
+                               'V', 'C', 'A', 'R', 'D', '_'
+
+
+typedef enum {
+    VCARD_DONE,
+    VCARD_NEXT,
+    VCARD_FAIL
+} VCardStatus;
+
+typedef enum {
+    VCARD_FILE_SYSTEM,
+    VCARD_VM,
+    VCARD_DIRECT
+} VCardType;
+
+typedef enum {
+    VCARD_POWER_ON,
+    VCARD_POWER_OFF
+} VCardPower;
+
+typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
+                                        VCardResponse **response);
+typedef VCardStatus (*VCardResetApplet)(VCard *card, int channel);
+typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
+typedef void (*VCardEmulFree) (VCardEmul *);
+typedef void (*VCardGetAtr) (VCard *, unsigned char *atr, int *atr_len);
+
+struct VCardBufferResponseStruct {
+    unsigned char *buffer;
+    int buffer_len;
+    unsigned char *current;
+    int len;
+};
+
+#endif
diff --git a/libcaccard/vevent.h b/libcaccard/vevent.h
new file mode 100644
index 0000000..f202ea8
--- /dev/null
+++ b/libcaccard/vevent.h
@@ -0,0 +1,26 @@
+/*
+ *
+ */
+#ifndef EVENT_H
+#define EVENT_H 1
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+VEvent *vevent_new(VEventType type, VReader *reader, VCard *card);
+void vevent_delete(VEvent *);
+
+/*
+ * VEvent queueing services
+ */
+void vevent_queue_vevent(VEvent *);
+void vevent_queue_init(void);
+
+/*
+ *  VEvent dequeing services
+ */
+VEvent *vevent_wait_next_vevent(void);
+VEvent *vevent_get_next_vevent(void);
+
+
+#endif
diff --git a/libcaccard/vreader.c b/libcaccard/vreader.c
new file mode 100644
index 0000000..85f96a8
--- /dev/null
+++ b/libcaccard/vreader.c
@@ -0,0 +1,515 @@
+/*
+ * emulate the reader
+ */
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "vevent.h"
+
+/*
+ * System includes
+ */
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * spice includes
+ */
+#include "mutex.h"
+
+struct VReaderStruct {
+    int    reference_count;
+    VCard *card;
+    char *name;
+    vreader_id_t id;
+    mutex_t lock;
+    VReaderEmul  *reader_private;
+    VReaderEmulFree reader_private_free;
+};
+
+/* manage locking */
+static inline void
+vreader_lock(VReader *reader)
+{
+    MUTEX_LOCK(reader->lock);
+}
+
+static inline void
+vreader_unlock(VReader *reader)
+{
+    MUTEX_UNLOCK(reader->lock);
+}
+
+/*
+ * vreader constructor
+ */
+VReader *
+vreader_new(const char *name, VReaderEmul *private,
+            VReaderEmulFree private_free)
+{
+    VReader *reader;
+
+    reader = (VReader *)malloc(sizeof(VReader));
+    if (reader == NULL) {
+        return NULL;
+    }
+    MUTEX_INIT(reader->lock);
+    reader->reference_count = 1;
+    reader->name = name ? strdup(name) : NULL;
+    reader->card = NULL;
+    reader->id = (vreader_id_t)-1;
+    reader->reader_private = private;
+    reader->reader_private_free = private_free;
+    return reader;
+}
+
+/* get a reference */
+VReader*
+vreader_reference(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    vreader_lock(reader);
+    reader->reference_count++;
+    vreader_unlock(reader);
+    return reader;
+}
+
+/* free a reference */
+void
+vreader_free(VReader *reader)
+{
+    if (reader == NULL) {
+        return;
+    }
+    vreader_lock(reader);
+    if (reader->reference_count-- > 1) {
+        vreader_unlock(reader);
+        return;
+    }
+    vreader_unlock(reader);
+    if (reader->card) {
+        vcard_free(reader->card);
+    }
+    if (reader->name) {
+        free(reader->name);
+    }
+    if (reader->reader_private_free) {
+        reader->reader_private_free(reader->reader_private);
+    }
+    free(reader);
+    return;
+}
+
+static VCard *
+vreader_get_card(VReader *reader)
+{
+    VCard *card;
+
+    vreader_lock(reader);
+    card = vcard_reference(reader->card);
+    vreader_unlock(reader);
+    return card;
+}
+
+VReaderStatus
+vreader_card_is_present(VReader *reader)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    vcard_free(card);
+    return VREADER_OK;
+}
+
+vreader_id_t
+vreader_get_id(VReader *reader)
+{
+    if (reader == NULL) {
+        return (vreader_id_t)-1;
+    }
+    return reader->id;
+}
+
+VReaderStatus
+vreader_set_id(VReader *reader, vreader_id_t id)
+{
+    if (reader == NULL) {
+        return VREADER_NO_CARD;
+    }
+    reader->id = id;
+    return VREADER_OK;
+}
+
+const char *
+vreader_get_name(VReader *reader)
+{
+    if (reader == NULL) {
+        return NULL;
+    }
+    return reader->name;
+}
+
+VReaderEmul *
+vreader_get_private(VReader *reader)
+{
+    return reader->reader_private;
+}
+
+static VReaderStatus
+vreader_reset(VReader *reader, VCardPower power, unsigned char *atr, int *len)
+{
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+    /*
+     * clean up our state
+     */
+    vcard_reset(card, power);
+    if (atr) {
+        vcard_get_atr(card, atr, len);
+    }
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+VReaderStatus
+vreader_power_on(VReader *reader, unsigned char *atr, int *len)
+{
+    return vreader_reset(reader, VCARD_POWER_ON, atr, len );
+}
+
+VReaderStatus
+vreader_power_off(VReader *reader)
+{
+    return vreader_reset(reader, VCARD_POWER_OFF, NULL, 0);
+}
+
+
+VReaderStatus
+vreader_xfr_bytes(VReader *reader,
+                  unsigned char *send_buf, int send_buf_len,
+                  unsigned char *receive_buf, int *receive_buf_len)
+{
+    VCardAPDU *apdu;
+    VCardResponse *response = NULL;
+    VCardStatus card_status;
+    unsigned short status;
+    VCard *card = vreader_get_card(reader);
+
+    if (card == NULL) {
+        return VREADER_NO_CARD;
+    }
+
+    apdu = vcard_apdu_new(send_buf, send_buf_len, &status);
+    if (apdu == NULL) {
+        response = vcard_make_response(status);
+        card_status = VCARD_DONE;
+    } else {
+        card_status = vcard_process_apdu(card, apdu, &response);
+    }
+    ASSERT(card_status == VCARD_DONE);
+    if (card_status == VCARD_DONE) {
+        int size = MIN(*receive_buf_len, response->b_total_len);
+        memcpy(receive_buf, response->b_data, size);
+        *receive_buf_len = size;
+    }
+    vcard_response_delete(response);
+    vcard_apdu_delete(apdu);
+    vcard_free(card); /* free our reference */
+    return VREADER_OK;
+}
+
+struct VReaderListStruct {
+    VReaderListEntry *head;
+    VReaderListEntry *tail;
+};
+
+struct VReaderListEntryStruct {
+    VReaderListEntry *next;
+    VReaderListEntry *prev;
+    VReader *reader;
+};
+
+
+static VReaderListEntry *
+vreader_list_entry_new(VReader *reader)
+{
+    VReaderListEntry *new_reader_list_entry;
+
+    new_reader_list_entry = (VReaderListEntry *)
+                               malloc(sizeof(VReaderListEntry));
+    if (new_reader_list_entry == NULL) {
+        return NULL;
+    }
+    new_reader_list_entry->next = NULL;
+    new_reader_list_entry->prev = NULL;
+    new_reader_list_entry->reader = vreader_reference(reader);
+    return new_reader_list_entry;
+}
+
+static void
+vreader_list_entry_delete(VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    vreader_free(entry->reader);
+    free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new(void)
+{
+    VReaderList *new_reader_list;
+
+    new_reader_list = (VReaderList *)malloc(sizeof(VReaderList));
+    if (new_reader_list == NULL) {
+        return NULL;
+    }
+    new_reader_list->head = NULL;
+    new_reader_list->tail = NULL;
+    return new_reader_list;
+}
+
+void
+vreader_list_delete(VReaderList *list)
+{
+    VReaderListEntry *current_entry;
+    VReaderListEntry *next_entry = NULL;
+    for (current_entry= vreader_list_get_first(list); current_entry;
+            current_entry=next_entry) {
+        next_entry = vreader_list_get_next(current_entry);
+        vreader_list_entry_delete(current_entry);
+    }
+    list->head = NULL;
+    list->tail = NULL;
+    free(list);
+}
+
+
+VReaderListEntry *
+vreader_list_get_first(VReaderList *list)
+{
+    return list ? list->head : NULL;
+}
+
+VReaderListEntry *
+vreader_list_get_next(VReaderListEntry *current)
+{
+    return current ? current->next : NULL;
+}
+
+VReader *
+vreader_list_get_reader(VReaderListEntry *entry)
+{
+    return entry ? vreader_reference(entry->reader) : NULL;
+}
+
+static void
+vreader_queue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    entry->next = NULL;
+    entry->prev = list->tail;
+    if (list->head) {
+        list->tail->next = entry;
+    } else {
+        list->head = entry;
+    }
+    list->tail = entry;
+}
+
+static void
+vreader_dequeue(VReaderList *list, VReaderListEntry *entry)
+{
+    if (entry == NULL) {
+        return;
+    }
+    if (entry->next == NULL) {
+        list->tail = entry->prev;
+    } else if (entry->prev == NULL) {
+        list->head = entry->next;
+    } else {
+        entry->prev->next = entry->next;
+        entry->next->prev = entry->prev;
+    }
+    if ((list->tail == NULL) || (list->head == NULL)) {
+        list->head = list->tail = NULL;
+    }
+    entry->next = entry->prev = NULL;
+}
+
+static VReaderList *vreader_list = NULL;
+static mutex_t vreader_list_mutex;
+
+static void
+vreader_list_init(void)
+{
+    vreader_list = vreader_list_new();
+    MUTEX_INIT(vreader_list_mutex);
+}
+
+static void
+vreader_list_lock(void)
+{
+    MUTEX_LOCK(vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock(void)
+{
+    MUTEX_UNLOCK(vreader_list_mutex);
+}
+
+static VReaderList *
+vreader_copy_list(VReaderList *list)
+{
+    VReaderList *new_list = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    new_list = vreader_list_new();
+    if (new_list == NULL) {
+        return NULL;
+    }
+    for (current_entry= vreader_list_get_first(list); current_entry;
+                       current_entry=vreader_list_get_next(current_entry)) {
+        VReader *reader = vreader_list_get_reader(current_entry);
+        VReaderListEntry *new_entry = vreader_list_entry_new(reader);
+
+        vreader_free(reader);
+        vreader_queue(new_list, new_entry);
+    }
+    return new_list;
+}
+
+VReaderList *
+vreader_get_reader_list(void)
+{
+    VReaderList *new_reader_list;
+
+    vreader_list_lock();
+    new_reader_list = vreader_copy_list(vreader_list);
+    vreader_list_unlock();
+    return new_reader_list;
+}
+
+VReader *
+vreader_get_reader_by_id(vreader_id_t id)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    if (id == (vreader_id_t) -1) {
+        return NULL;
+    }
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (creader->id == id) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+VReader *
+vreader_get_reader_by_name(const char *name)
+{
+    VReader *reader = NULL;
+    VReaderListEntry *current_entry = NULL;
+
+    vreader_list_lock();
+    for (current_entry = vreader_list_get_first(vreader_list); current_entry;
+            current_entry = vreader_list_get_next(current_entry)) {
+        VReader *creader = vreader_list_get_reader(current_entry);
+        if (strcmp(creader->name, name) == 0) {
+            reader = creader;
+            break;
+        }
+        vreader_free(creader);
+    }
+    vreader_list_unlock();
+    return reader;
+}
+
+/* called from card_emul to initialize the readers */
+VReaderStatus
+vreader_add_reader(VReader *reader)
+{
+    VReaderListEntry *reader_entry;
+
+    reader_entry = vreader_list_entry_new(reader);
+    if (reader_entry == NULL) {
+        return VREADER_OUT_OF_MEMORY;
+    }
+    vreader_list_lock();
+    vreader_queue(vreader_list, reader_entry);
+    vreader_list_unlock();
+    vevent_queue_vevent(vevent_new(VEVENT_READER_INSERT, reader, NULL));
+    return VREADER_OK;
+}
+
+
+VReaderStatus
+vreader_remove_reader(VReader *reader)
+{
+    VReaderListEntry *current_entry;
+
+    vreader_list_lock();
+    for (current_entry= vreader_list_get_first(vreader_list); current_entry;
+                       current_entry=vreader_list_get_next(current_entry)) {
+        if (current_entry->reader == reader) {
+            break;
+        }
+    }
+    vreader_dequeue(vreader_list, current_entry);
+    vreader_list_unlock();
+    vreader_list_entry_delete(current_entry);
+    vevent_queue_vevent(vevent_new(VEVENT_READER_REMOVE, reader, NULL));
+    return VREADER_OK;
+}
+
+
+/*
+ * insert/remove a new card. for removal, card == NULL
+ */
+VReaderStatus
+vreader_insert_card(VReader *reader, VCard *card)
+{
+    vreader_lock(reader);
+    if (reader->card) {
+        /* decrement reference count */
+        vcard_free(reader->card);
+        reader->card = NULL;
+    }
+    reader->card = vcard_reference(card);
+    vreader_unlock(reader);
+    vevent_queue_vevent(vevent_new(
+        card ? VEVENT_CARD_INSERT : VEVENT_CARD_REMOVE, reader, card));
+    return VREADER_OK;
+}
+
+/*
+ * initialize all the static reader structures
+ */
+void
+vreader_init(void)
+{
+    vreader_list_init();
+}
+
diff --git a/libcaccard/vreader.h b/libcaccard/vreader.h
new file mode 100644
index 0000000..f3f9434
--- /dev/null
+++ b/libcaccard/vreader.h
@@ -0,0 +1,53 @@
+/*
+ *
+ */
+
+#ifndef VREADER_H
+#define VREADER_H 1
+
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+/*
+ * calls for reader front end
+ */
+VReaderStatus vreader_power_on(VReader *reader, unsigned char *atr, int *len);
+VReaderStatus vreader_power_off(VReader *reader);
+VReaderStatus vreader_xfr_bytes(VReader *reader, unsigned char *send_buf,
+                                int send_buf_len, unsigned char *receive_buf,
+                                int *receive_buf_len);
+
+/* constructor */
+VReader *vreader_new(const char *readerName, VReaderEmul *emul_private,
+                     VReaderEmulFree private_free);
+/* get a new reference to a reader */
+VReader *vreader_reference(VReader *reader);
+/* "destructor" (readers are reference counted) */
+void vreader_free(VReader *reader);
+
+/* accessors */
+VReaderEmul *vreader_get_private(VReader *);
+VReaderStatus vreader_card_is_present(VReader *reader);
+const char *vreader_get_name(VReader *reader);
+vreader_id_t vreader_get_id(VReader *reader);
+VReaderStatus vreader_set_id(VReader *reader, vreader_id_t id);
+
+/* list operations */
+VReaderList *vreader_get_reader_list(void);
+void vreader_list_delete(VReaderList *list);
+VReader *vreader_list_get_reader(VReaderListEntry *entry);
+VReaderListEntry *vreader_list_get_first(VReaderList *list);
+VReaderListEntry *vreader_list_get_next(VReaderListEntry *list);
+VReader *vreader_get_reader_by_id(vreader_id_t id);
+VReader *vreader_get_reader_by_name(const char *name);
+
+/*
+ * list tools for vcard_emul
+ */
+void vreader_init(void);
+VReaderStatus vreader_add_reader(VReader *reader);
+VReaderStatus vreader_remove_reader(VReader *reader);
+VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
+
+#endif
diff --git a/libcaccard/vreadert.h b/libcaccard/vreadert.h
new file mode 100644
index 0000000..4c01259
--- /dev/null
+++ b/libcaccard/vreadert.h
@@ -0,0 +1,23 @@
+/*
+ *
+ */
+
+#ifndef VREADERT_H
+#define VREADERT_H 1
+
+typedef enum {
+    VREADER_OK=0,
+    VREADER_NO_CARD,
+    VREADER_OUT_OF_MEMORY
+} VReaderStatus;
+
+typedef unsigned int vreader_id_t;
+typedef struct VReaderStruct VReader;
+typedef struct VReaderListStruct VReaderList;
+typedef struct VReaderListEntryStruct VReaderListEntry;
+
+typedef struct VReaderEmulStruct VReaderEmul;
+typedef void (*VReaderEmulFree)(VReaderEmul *);
+
+#endif
+
diff --git a/libcaccard/vscard_common.h b/libcaccard/vscard_common.h
new file mode 100644
index 0000000..1a4afd7
--- /dev/null
+++ b/libcaccard/vscard_common.h
@@ -0,0 +1,131 @@
+/* Virtual Smart Card protocol definition
+ *
+ * This protocol is between a host implementing a group of virtual smart card
+ * reader, and a client implementing a virtual smart card, or passthrough to
+ * a real card.
+ *
+ * The current implementation passes the raw APDU's from 7816 and additionally
+ * contains messages to setup and teardown readers, handle insertion and
+ * removal of cards, negotiate the protocol and provide for error responses.
+ *
+ * Copyright (c) 2010 Red Hat.
+ *
+ * This code is licensed under the LGPL.
+ */
+
+#ifndef _VSCARD_COMMON_H
+#define _VSCARD_COMMON_H
+
+#include <stdint.h>
+
+#define VERSION_MAJOR_BITS 11
+#define VERSION_MIDDLE_BITS 11
+#define VERSION_MINOR_BITS 10
+
+#define MAKE_VERSION(major, middle, minor) \
+     (  (major  << (VERSION_MINOR_BITS + VERSION_MIDDLE_BITS)) \
+      | (middle <<  VERSION_MINOR_BITS) \
+      | (minor)  )
+
+/** IMPORTANT NOTE on VERSION
+ *
+ * The version below MUST be changed whenever a change in this file is made.
+ *
+ * The last digit, the minor, is for bug fix changes only.
+ *
+ * The middle digit is for backward / forward compatible changes, updates
+ * to the existing messages, addition of fields.
+ *
+ * The major digit is for a breaking change of protocol, presumably
+ * something that cannot be accomodated with the existing protocol.
+ */
+
+#define VSCARD_VERSION MAKE_VERSION(0,0,1)
+
+#define VSCARD_UNDEFINED_READER_ID -1
+#define VSCARD_MINIMAL_READER_ID    0
+
+typedef enum {
+    VSC_Init,
+    VSC_Error,
+    VSC_ReaderAdd,
+    VSC_ReaderAddResponse,
+    VSC_ReaderRemove,
+    VSC_ATR,
+    VSC_CardRemove,
+    VSC_APDU,
+    VSC_Reconnect
+} VSCMsgType;
+
+typedef enum {
+    VSC_GENERAL_ERROR=1,
+    VSC_CANNOT_ADD_MORE_READERS,
+} VSCErrorCode;
+
+typedef uint32_t reader_id_t;
+
+typedef struct VSCMsgHeader {
+    VSCMsgType type;
+    reader_id_t   reader_id;
+    uint32_t   length;
+    uint8_t    data[0];
+} VSCMsgHeader;
+
+/* VSCMsgInit               Client <-> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * */
+typedef struct VSCMsgInit {
+    uint32_t   version;
+} VSCMsgInit;
+
+/* VSCMsgError              Client <-> Host
+ * */
+typedef struct VSCMsgError {
+    uint32_t   code;
+} VSCMsgError;
+
+/* VSCMsgReaderAdd          Client -> Host
+ * Host replies with allocated reader id in ReaderAddResponse
+ * name - name of the reader on client side.
+ * */
+typedef struct VSCMsgReaderAdd {
+    uint8_t    name[0];
+} VSCMsgReaderAdd;
+
+/* VSCMsgReaderAddResponse  Host -> Client
+ * Reply to ReaderAdd
+ * */
+typedef struct VSCMsgReaderAddResponse {
+} VSCMsgReaderAddResponse;
+
+/* VSCMsgReaderRemove       Client -> Host
+ * */
+typedef struct VSCMsgReaderRemove {
+} VSCMsgReaderRemove;
+
+/* VSCMsgATR                Client -> Host
+ * Answer to reset. Sent for card insertion or card reset.
+ * */
+typedef struct VSCMsgATR {
+    uint8_t     atr[0];
+} VSCMsgATR;
+
+/* VSCMsgCardRemove         Client -> Host
+ * */
+typedef struct VSCMsgCardRemove {
+} VSCMsgCardRemove;
+
+/* VSCMsgAPDU               Client <-> Host
+ * */
+typedef struct VSCMsgAPDU {
+    uint8_t    data[0];
+} VSCMsgAPDU;
+
+/* VSCMsgReconnect          Host -> Client
+ * */
+typedef struct VSCMsgReconnect {
+    uint32_t   ip;
+    uint16_t   port;
+} VSCMsgReconnect;
+
+#endif
diff --git a/libcaccard/vscclient.c b/libcaccard/vscclient.c
new file mode 100644
index 0000000..4dd8a35
--- /dev/null
+++ b/libcaccard/vscclient.c
@@ -0,0 +1,710 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <netinet/in.h>
+
+#include "vscard_common.h"
+
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "vevent.h"
+#include "passthru.h"
+
+#include "mutex.h"
+
+int verbose = 0;
+
+int sock;
+
+static void
+print_byte_array (
+    uint8_t *arrBytes,
+    unsigned int nSize
+) {
+    int i;
+    for (i=0; i < nSize; i++) {
+        printf ("%02X ", arrBytes[i]);
+    }
+    printf ("\n");
+}
+
+static void
+print_usage (void) {
+    printf ("vscclient [-c <certname> .. -e <emul_args> -d <level>%s] "
+            "<host> <port> \n",
+#ifdef USE_PASSTHRU
+    " -p");
+    printf (" -p use passthrough mode\n");
+#else
+   "");
+#endif
+    vcard_emul_usage();
+}
+
+static char*
+ip_numeric_to_char(
+    uint32_t ip
+) {
+    char buf[4*4];
+
+    sprintf(buf, "%d.%d.%d.%d", (ip & 0xff000000) >> 24, (ip & 0xff0000) >> 16,
+        (ip & 0xff00) >> 8, ip & 0xff);
+    return strdup(buf);
+}
+
+static mutex_t write_lock;
+
+static int
+send_msg (
+    VSCMsgType type,
+    uint32_t reader_id,
+    const void *msg,
+    unsigned int length
+) {
+    int rv;
+    VSCMsgHeader mhHeader;
+
+    MUTEX_LOCK(write_lock);
+
+    if (verbose > 10) {
+        printf("sending type=%d id=%d, len =%d (0x%x)\n",
+               type, reader_id, length, length);
+    }
+
+    mhHeader.type = type;
+    mhHeader.reader_id = 0;
+    mhHeader.length = length;
+    rv = write (
+        sock,
+        &mhHeader,
+        sizeof (mhHeader)
+    );
+    if (rv < 0) {
+        /* Error */
+        printf ("write header error\n");
+        close (sock);
+        MUTEX_UNLOCK(write_lock);
+        return (16);
+    }
+    rv = write (
+        sock,
+        msg,
+        length
+    );
+    if (rv < 0) {
+        /* Error */
+        printf ("write error\n");
+        close (sock);
+        MUTEX_UNLOCK(write_lock);
+        return (16);
+    }
+    MUTEX_UNLOCK(write_lock);
+
+    return (0);
+}
+
+static VReader *pending_reader = NULL;
+static mutex_t pending_reader_lock;
+static condition_t pending_reader_condition;
+
+#define MAX_ATR_LEN 40
+static void *
+event_thread(void *arg)
+{
+    unsigned char atr[ MAX_ATR_LEN];
+    int atr_len = MAX_ATR_LEN;
+    VEvent *event = NULL;
+    unsigned int reader_id;
+
+
+    while (1) {
+        const char *reader_name;
+
+        event = vevent_wait_next_vevent();
+        if (event == NULL) {
+            break;
+        }
+        reader_id = vreader_get_id(event->reader);
+        if (reader_id == VSCARD_UNDEFINED_READER_ID &&
+            event->type != VEVENT_READER_INSERT) {
+            /* ignore events from readers qemu has rejected */
+            /* if qemu is still deciding on this reader, wait to see if need to
+             * forward this event */
+            MUTEX_LOCK(pending_reader_lock);
+            if (!pending_reader || (pending_reader != event->reader)) {
+                /* wasn't for a pending reader, this reader has already been
+                 * rejected by qemu */
+                MUTEX_UNLOCK(pending_reader_lock);
+                vevent_delete(event);
+                continue;
+            }
+            /* this reader hasn't been told it's status from qemu yet, wait for
+             * that status */
+            while (pending_reader != NULL) {
+                CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+            }
+            MUTEX_UNLOCK(pending_reader_lock);
+            /* now recheck the id */
+            reader_id = vreader_get_id(event->reader);
+            if (reader_id == VSCARD_UNDEFINED_READER_ID) {
+                /* this reader was rejected */
+                vevent_delete(event);
+                continue;
+            }
+            /* reader was accepted, now forward the event */
+        }
+        switch (event->type) {
+        case VEVENT_READER_INSERT:
+            /* tell qemu to insert a new CCID reader */
+            /* wait until qemu has responded to our first reader insert
+             * before we send a second. That way we won't confuse the responses
+             * */
+            MUTEX_LOCK(pending_reader_lock);
+            while (pending_reader != NULL) {
+                CONDITION_WAIT(pending_reader_condition,pending_reader_lock);
+            }
+            pending_reader = vreader_reference(event->reader);
+            MUTEX_UNLOCK(pending_reader_lock);
+            reader_name = vreader_get_name(event->reader);
+            if (verbose > 10) {
+                printf (" READER INSERT: %s\n", reader_name);
+            }
+            send_msg (
+                VSC_ReaderAdd,
+                reader_id, /* currerntly VSCARD_UNDEFINED_READER_ID */
+                NULL, 0
+                /*reader_name,
+                strlen(reader_name) */
+            );
+
+            break;
+        case VEVENT_READER_REMOVE:
+            /* future, tell qemu that an old CCID reader has been removed */
+            if (verbose > 10) {
+                printf (" READER REMOVE: %d \n", reader_id);
+            }
+            send_msg(
+                VSC_ReaderRemove,
+                reader_id,
+                NULL,
+                0
+            );
+            break;
+        case VEVENT_CARD_INSERT:
+            /* get the ATR (intended as a response to a power on from the
+             * reader */
+            atr_len = MAX_ATR_LEN;
+            vreader_power_on(event->reader, atr, &atr_len);
+            /* ATR call functions as a Card Insert event */
+            if (verbose > 10) {
+                printf (" CARD INSERT %d: ", reader_id);
+                print_byte_array (atr, atr_len);
+            }
+            send_msg (
+                VSC_ATR,
+                reader_id,
+                atr,
+                atr_len
+            );
+            break;
+        case VEVENT_CARD_REMOVE:
+            // Card removed
+            if (verbose > 10) {
+                printf (" CARD REMOVE %d: \n", reader_id);
+            }
+            send_msg (
+                VSC_CardRemove,
+                reader_id,
+                NULL,
+                0
+            );
+            break;
+        default:
+            break;
+        }
+        vevent_delete(event);
+    }
+    return NULL;
+}
+
+
+static unsigned int
+get_id_from_string(char *string, unsigned int default_id)
+{
+    unsigned int id = atoi(string);
+
+    /* don't accidentally swith to zero because no numbers have been supplied */
+    if ((id == 0) && *string != '0') {
+        return default_id;
+    }
+    return id;
+}
+
+static void
+do_command(void)
+{
+    char inbuf[255];
+    char *string;
+    VCardEmulError error;
+    static unsigned int default_reader_id = 0;
+    unsigned int reader_id;
+    VReader *reader = NULL;
+
+    reader_id = default_reader_id;
+    string = fgets(inbuf, sizeof(inbuf), stdin);
+    if (string != NULL) {
+        if (strncmp(string,"exit",4) == 0) {
+            /* remove all the readers */
+            VReaderList *list = vreader_get_reader_list();
+            VReaderListEntry *reader_entry;
+            printf("Active Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id == -1) {
+                    continue;
+                }
+                /* be nice and signal card removal first (qemu probably should
+                 * do this itself) */
+                if (vreader_card_is_present(reader) == VREADER_OK) {
+                    send_msg (
+                        VSC_CardRemove,
+                        reader_id,
+                        NULL,
+                        0
+                    );
+                }
+                send_msg (
+                    VSC_ReaderRemove,
+                    reader_id,
+                    NULL,
+                    0
+                );
+            }
+            exit(0);
+        } else if (strncmp(string,"insert",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7], reader_id);
+            }
+            reader = vreader_get_reader_by_id(reader_id);
+            error = vcard_emul_force_card_insert(reader);
+            printf("insert %s, returned %d\n", reader ? vreader_get_name(reader)
+                                               : "invalid reader", error);
+        } else if (strncmp(string,"remove",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7], reader_id);
+            }
+            reader = vreader_get_reader_by_id(reader_id);
+            error = vcard_emul_force_card_remove(reader);
+            printf("remove %s, returned %d\n", reader ? vreader_get_name(reader)
+                                               : "invalid reader", error);
+        } else if (strncmp(string,"select",6) == 0) {
+            if (string[6] == ' ') {
+                reader_id = get_id_from_string(&string[7],
+                                               VSCARD_UNDEFINED_READER_ID);
+            }
+            if (reader_id != VSCARD_UNDEFINED_READER_ID) {
+                reader = vreader_get_reader_by_id(reader_id);
+            }
+            if (reader) {
+                printf("Selecting reader %d, %s\n", reader_id,
+                        vreader_get_name(reader));
+                default_reader_id = reader_id;
+            } else {
+                printf("Reader with id %d not found\n", reader_id);
+            }
+        } else if (strncmp(string,"debug",5) == 0) {
+            if (string[5] == ' ') {
+                verbose = get_id_from_string(&string[6],0);
+            }
+            printf ("debug level = %d\n", verbose);
+        } else if (strncmp(string,"list",4) == 0) {
+            VReaderList *list = vreader_get_reader_list();
+            VReaderListEntry *reader_entry;
+            printf("Active Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id == -1) {
+                    continue;
+                }
+                printf("%3d %s %s\n",reader_id,
+                       vreader_card_is_present(reader) == VREADER_OK ?
+                       "CARD_PRESENT": "            ",
+                       vreader_get_name(reader));
+            }
+            printf("Inactive Readers:\n");
+            for (reader_entry = vreader_list_get_first(list); reader_entry;
+                 reader_entry = vreader_list_get_next(reader_entry)) {
+                VReader *reader = vreader_list_get_reader(reader_entry);
+                vreader_id_t reader_id;
+                reader_id=vreader_get_id(reader);
+                if (reader_id != -1) {
+                    continue;
+                }
+
+                printf("INA %s %s\n",
+                       vreader_card_is_present(reader) == VREADER_OK ?
+                       "CARD_PRESENT": "            ",
+                       vreader_get_name(reader));
+            }
+        } else if (*string != 0) {
+            printf("valid commands: \n");
+            printf("insert [reader_id]\n");
+            printf("remove [reader_id]\n");
+            printf("select reader_id\n");
+            printf("list\n");
+            printf("debug [level]\n");
+            printf("exit\n");
+        }
+    }
+    vreader_free(reader);
+    printf("> ");
+    fflush(stdout);
+}
+
+
+#define APDUBufSize 270
+
+// just for ease of parsing command line arguments.
+#define MAX_CERTS 100
+
+static int
+connect_to_qemu (
+    const char *ip,
+    uint32_t port
+) {
+    struct addrinfo hints;
+    struct addrinfo* server;
+    int ret;
+    char port_str[10];
+
+    sock = socket (
+        AF_INET,
+        SOCK_STREAM,
+        0
+    );
+    if (sock < 0) {
+        // Error
+        printf ("Error opening socket!\n");
+    }
+
+    memset(&hints, 0, sizeof(struct addrinfo));
+    hints.ai_family = AF_INET;
+    hints.ai_socktype = SOCK_STREAM;
+    hints.ai_flags = 0;
+    hints.ai_protocol = 0;          /* Any protocol */
+    snprintf(port_str, sizeof(port_str) - 1, "%d", port);
+
+    ret = getaddrinfo(ip, port_str, &hints, &server);
+
+    if (ret != 0) {
+        printf ("getaddrinfo failed\n");
+        return (5);
+    }
+
+    if (connect (
+            sock,
+            server->ai_addr,
+            server->ai_addrlen
+        ) < 0
+    ) {
+        // Error
+        printf ("Could not connect\n");
+        return (5);
+    }
+    if (verbose) {
+        printf ("Connected (sizeof Header=%zd)!\n", sizeof (VSCMsgHeader));
+    }
+    return sock;
+}
+
+int
+main (
+    int argc,
+    char *argv[]
+) {
+    char* qemu_ip;
+    uint16_t qemu_port;
+    VSCMsgHeader mhHeader;
+    VSCMsgError *error_msg;
+
+    int rv;
+    int dwSendLength;
+    int dwRecvLength;
+    uint8_t pbRecvBuffer[APDUBufSize];
+    uint8_t pbSendBuffer[APDUBufSize];
+     VReaderStatus reader_status;
+    VReader *reader = NULL;
+    VCardEmulOptions *command_line_options = NULL;
+    pthread_t thread_id;
+    int passthru = 0;
+
+    char* cert_names[MAX_CERTS];
+    char* emul_args = NULL;
+    int cert_count = 0;
+    int c;
+
+    while ((c = getopt(argc, argv, "c:e:pd:")) != -1) {
+        switch (c) {
+            case 'c':
+                if (cert_count >= MAX_CERTS) {
+                    printf("too many certificates (max = %d)\n", MAX_CERTS);
+                    exit (5);
+                }
+                cert_names[cert_count++] = optarg;
+                break;
+            case 'e':
+                emul_args = optarg;
+                break;
+            case 'p':
+#ifdef USE_PASSTHRU
+                passthru = 1;
+#else
+                print_usage();
+                exit(4);
+#endif
+                break;
+            case 'd':
+                verbose = get_id_from_string(optarg,1);
+                break;
+        }
+    }
+
+    if (argc - optind != 2) {
+        print_usage();
+        exit (4);
+    }
+
+    if (!passthru && cert_count > 0) {
+        char *new_args;
+        int len, i;
+        /* if we've given some -c options, we clearly we want do so some
+         * software emulation.  add that emulation now. this is NSS Emulator
+         * specific */
+        if (emul_args == NULL) {
+            emul_args = (char*)"db=\"/etc/pki/nssdb\"";
+        }
+#define SOFT_STRING ",soft=(,Virtual Reader,CAC,,"
+             /* 2 == close paren & null */
+        len = strlen(emul_args) + strlen(SOFT_STRING) + 2;
+        for (i=0; i < cert_count; i++) {
+            len +=strlen(cert_names[i])+1; /* 1 == comma */
+        }
+        new_args = malloc(len);
+        strcpy(new_args,emul_args);
+        strcat(new_args,SOFT_STRING);
+        for (i=0; i < cert_count; i++) {
+            strcat(new_args,cert_names[i]);
+            strcat(new_args,",");
+        }
+        strcat(new_args,")");
+        emul_args = new_args;
+    }
+    if (emul_args) {
+#ifdef USE_PASSTHRU
+        command_line_options = passthru ? passthru_emul_options(emul_args) :
+#else
+        command_line_options =
+#endif
+                                          vcard_emul_options(emul_args);
+    }
+
+    qemu_ip = strdup(argv[argc - 2]);
+    qemu_port = (uint16_t)atoi(argv[argc -1]);
+    sock = connect_to_qemu(qemu_ip, qemu_port);
+
+    /* remove whatever reader might be left in qemu,
+     * in case of a unclean previous exit. */
+    send_msg(
+        VSC_ReaderRemove,
+        VSCARD_MINIMAL_READER_ID,
+        NULL,
+        0
+    );
+
+    MUTEX_INIT(write_lock);
+    MUTEX_INIT(pending_reader_lock);
+    CONDITION_INIT(pending_reader_condition);
+
+#ifdef USE_PASSTHRU
+    if (passthru) {
+        passthru_emul_init(command_line_options);
+    } else
+#endif
+        vcard_emul_init(command_line_options);
+
+    /* launch the event_thread. This will trigger reader adds for all the
+     * existing readers */
+    rv = pthread_create(&thread_id, NULL, event_thread, reader);
+    if (rv < 0) {
+        perror("pthread_create");
+        exit (1);
+    }
+
+    printf("> ");
+    fflush(stdout);
+
+    do {
+        fd_set fds;
+
+        FD_ZERO(&fds);
+        FD_SET(1,&fds);
+        FD_SET(sock,&fds);
+
+        /* waiting on input from the socket */
+        rv = select(sock+1, &fds, NULL, NULL, NULL);
+        if (rv < 0) {
+            /* handle error */
+            perror("select");
+            return (7);
+        }
+        if (FD_ISSET(1,&fds)) {
+            do_command();
+        }
+        if (!FD_ISSET(sock,&fds)) {
+            continue;
+        }
+
+        rv = read (
+            sock,
+            &mhHeader,
+            sizeof (mhHeader)
+        );
+        if (rv < sizeof(mhHeader)) {
+            /* Error */
+            if (rv < 0) {
+                perror("header read error\n");
+            } else {
+                printf ("header short read %d\n", rv);
+            }
+            return (8);
+        }
+        if (verbose) {
+            printf ("Header: type=%d, reader_id=%d length=%d (0x%x)\n",
+                    mhHeader.type, mhHeader.reader_id, mhHeader.length,
+                                               mhHeader.length);
+        }
+        switch (mhHeader.type) {
+            case VSC_APDU:
+                rv = read (
+                    sock,
+                    pbSendBuffer,
+                    mhHeader.length
+                );
+                if (rv < 0) {
+                    /* Error */
+                    printf ("read error\n");
+                    close (sock);
+                    return (8);
+                }
+                if (verbose) {
+                    printf (" recv APDU: ");
+                    print_byte_array (pbSendBuffer, mhHeader.length);
+                }
+                /* Transmit recieved APDU */
+                dwSendLength = mhHeader.length;
+                dwRecvLength = sizeof(pbRecvBuffer);
+                reader = vreader_get_reader_by_id(mhHeader.reader_id);
+                reader_status = vreader_xfr_bytes(reader,
+                    pbSendBuffer, dwSendLength,
+                    pbRecvBuffer, &dwRecvLength);
+                if (reader_status == VREADER_OK) {
+                    mhHeader.length = dwRecvLength;
+                if (verbose) {
+                    printf (" send response: ");
+                    print_byte_array (pbRecvBuffer, mhHeader.length);
+                }
+                    send_msg (
+                        VSC_APDU,
+                        mhHeader.reader_id,
+                        pbRecvBuffer,
+                        dwRecvLength
+                    );
+                } else {
+                       rv = reader_status; /* warning: not meaningful */
+                    send_msg (
+                        VSC_Error,
+                        mhHeader.reader_id,
+                        &rv,
+                        sizeof (uint32_t)
+                    );
+                }
+                vreader_free(reader);
+                reader = NULL; /* we've freed it, don't use it by accident
+                                  again */
+                break;
+            case VSC_Reconnect:
+                {
+                    VSCMsgReconnect reconnect;
+
+                    if (read(sock, (char*)&reconnect, mhHeader.length) < 0) {
+                        printf ("read error\n");
+                        close (sock);
+                        return (8);
+                    }
+                    if (reconnect.ip != 0) {
+                        reconnect.ip = ntohl(reconnect.ip);
+                        free(qemu_ip);
+                        qemu_ip = ip_numeric_to_char(reconnect.ip);
+                        qemu_port = reconnect.port;
+                    } else {
+                        printf("info: reconnect with no target ip:port: "
+                               "bumping port by one and reconnecting\n");
+                        qemu_port = qemu_port + 1;
+                    }
+                    /* sent when qemu is migrating, we need to close the socket
+                     * and reconnect. */
+                    close(sock);
+                    printf("reconnecting to %s:%d\n", qemu_ip, qemu_port);
+                    sock = connect_to_qemu(qemu_ip, qemu_port);
+                }
+                break;
+            case VSC_ReaderAddResponse:
+               MUTEX_LOCK(pending_reader_lock);
+                if (pending_reader) {
+                    vreader_set_id(pending_reader, mhHeader.reader_id);
+                    vreader_free(pending_reader);
+                    pending_reader = NULL;
+                    CONDITION_NOTIFY(pending_reader_condition);
+                }
+                MUTEX_UNLOCK(pending_reader_lock);
+                break;
+            case VSC_Error:
+                rv = read (
+                    sock,
+                    pbSendBuffer,
+                    mhHeader.length
+                );
+                error_msg = (VSCMsgError *) pbSendBuffer;
+                printf("error: qemu refused to add reader\n");
+                if (error_msg->code == VSC_CANNOT_ADD_MORE_READERS) {
+                    /* clear pending reader, qemu can't handle any more */
+                    MUTEX_LOCK(pending_reader_lock);
+                    if (pending_reader) {
+                        pending_reader = NULL;
+                        /* make sure the event loop doesn't hang */
+                        CONDITION_NOTIFY(pending_reader_condition);
+                    }
+                    MUTEX_UNLOCK(pending_reader_lock);
+                }
+                break;
+            default:
+                printf ("Default\n");
+                return 0;
+        }
+    } while (rv >= 0);
+
+
+    return (0);
+}
