summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile60
-rw-r--r--README107
-rw-r--r--cac.c354
-rw-r--r--card_7816.c664
-rw-r--r--card_7816.h67
-rw-r--r--card_7816t.h163
-rw-r--r--event.c108
-rw-r--r--event.h20
-rw-r--r--eventt.h27
-rw-r--r--link_test.c18
-rw-r--r--mutex.h43
-rw-r--r--scard_common.h16
-rw-r--r--vcard.c289
-rw-r--r--vcard.h68
-rw-r--r--vcard_emul.h67
-rw-r--r--vcard_emul_nss.c756
-rw-r--r--vcardt.h56
-rw-r--r--vreader.c444
-rw-r--r--vreader.h45
-rw-r--r--vreadert.h22
-rw-r--r--vscclient.c275
21 files changed, 3669 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..f031918
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,60 @@
+#
+#
+# This needs to be turned into automake system
+#
+#
+
+SOURCES= \
+ cac.c \
+ card_7816.c \
+ event.c \
+ vcard_emul_nss.c \
+ vcard.c \
+ vreader.c \
+ $(NULL)
+
+INCLUDE = \
+ vcard.h \
+ vcardt.h \
+ card_7816.h \
+ card_7816t.h \
+ vreader.h \
+ vreadert.h \
+ event.h \
+ eventt.h \
+ vcard_emul.h
+
+CFLAGS=`pkg-config --cflags nss` -g
+LDFLAGS=`pkg-config --libs nss`
+OBJS = ${SOURCES:.c=.o}
+
+all: $(OBJS) vscclient
+
+
+vscclient: vscclient.c $(OBJS)
+ cc -o vscclient vscclient.c $(CFLAGS) $(OBJS) $(LDFLAGS)
+
+
+clean:
+ rm -f $(OBJS) link_test __htest.c __test.o vscclient
+
+#
+# Test targets....
+#
+
+# make sure our includes are self supporting
+header_test:
+ @for i in $(INCLUDE) ; do \
+ echo "#include \"$$i\"" > __htest.c ;\
+ echo "#include \"$$i\"" >> __htest.c ;\
+ cat __htest.c ; \
+ cc -c __htest.c ;\
+ done
+ @rm __htest.c __htest.o
+
+# make sure we don't have any missing symbols
+link_test: link_test.c $(OBJS)
+ cc -o link_test link_test.c $(OBJS) $(LDFLAGS)
+
+# Do all the tests at once
+tests: all link_test header_test
diff --git a/README b/README
new file mode 100644
index 0000000..b003171
--- /dev/null
+++ b/README
@@ -0,0 +1,107 @@
+How the code is structured to support a CCID reader front end, though any
+reader that support 7816 cards will need similiar functions.
+
+The calls that the virtual reader front end can call are:
+
+VReaderStatus
+vreader_get_reader_list(VReaderList **list) -
+
+returns a list of virtual readers. These readers may map to physical devices,
+or simulated devices depending on vcard the back end. Each reader in the list
+should represent a reader to the virtuall machine. Virtual USB address mapping
+and device naming is left to the CCID reader front end.
+
+VReaderStatus
+v_reader_power_on(VReader *reader, char *atr, int **len) -
+
+simulate a card power on. Virtual cards do not care about the actual voltage
+and other physical parameters, but it does care that the card is actually on
+or off. Power on returns the ATR of the virtual card.
+
+VReaderStatus
+vreader_power_off(VReader *reader) -
+
+power off a virtual card.
+
+VReaderStatus
+vreader_xfer_bytes(VReader *reader, unsigne char *send_buf, int send_buf_len,
+ unsigned char *receive_buf, int receive_buf_len) -
+
+send a raw apdu to a card and get a response. The CCID front end should return
+the response back to the 'host'.
+
+VReaderStatus
+vreader_wait_for_status_change(VReaderList *list) -
+
+should be made on a new thread by the reader front end. The back end will
+return if there are any state changes to the readers on the list (including
+reader insertions, reader removals, card insertions, or card removals). The
+reader front end can use this to generate Interupt-IN messages.
+
+
+---------------------------------------------------------------------------
+
+The code creates a virtual CAC card. Most of the simulation is APDU processess.
+card_7816.c implements the common instructions processing for 7816 cards.
+Currently only VM cards are simulated. No Secure messaging APDUs are simulated,
+however. These are usually used for card provisioning and requires cards and
+back end servers to share keys.
+
+
+Describe the ADPU structure.
+
+
+Describe the Response structure.
+
+
+Describe the applet select mechanism.
+
+
+
+card_cac.c implements the cac specific applets. Only those applets needed by
+the coolkey pkcs#11 driver on the guest have been implemented. To support the
+full range CAC middleware, a complete CAC card according to the CAC specs
+should be implemented here. To create a virtual piv or virtual coolkey, a
+similiar .c file could be added which implements the applets on those cards.
+
+
+-----------------------------------------------------------------------------
+
+card_emul back end. The following calls make up the smart card back end. The
+current implementation uses NSS so supply the appropriate functionality. This
+allows not only real smart cards on the host to be used, but also smart cards
+simulated by certs and keys in the users NSS database, or in PEM files. The NSS
+implementation could be replaced by a CAPI or direct to PKCS #11
+implementation.
+
+/*
+ * Login functions
+ */
+/* return the number of login attempts still possible on the card. if unknown, return -1 */
+int card_emul_get_login_count(VCard *card);
+/* login into the card, return the 7816 status word (sw2 || sw1) */
+VCard7816Status card_emul_login(VCard *card, unsigned char *pin, int pin_len);
+
+/*
+ * key functions
+ */
+
+/* delete a key */
+void card_emul_delete_key(VCardKey *key);
+/* RSA sign/decrypt with the key, signature happens 'in place' */
+VCard7816Status card_emul_rsa_op(VCard *card, VCardKey *key,
+ unsigned char *buffer, int buffer_size);
+
+void card_emul_reset(VCard *card, VCardPower power);
+
+/*
+ * still need initialize, card insertion/removal status, get the list of valid reader
+ * insertion/removal may need to be a thread that returns insertion/removal events
+ */
+void card_emul_initialize();
+
+
+
+
+
+
diff --git a/cac.c b/cac.c
new file mode 100644
index 0000000..5049537
--- /dev/null
+++ b/cac.c
@@ -0,0 +1,354 @@
+/*
+ * impement the applets for the CAC card.
+ */
+#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 {
+ CACPKIAppletData pki_data;
+};
+
+/*
+ * 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;
+}
+
+
+static VCardStatus
+cac_applet_pki_process_apdu(VCard *card, VCardAPDU *apdu, VCardResponse **response)
+{
+ CACPKIAppletData *pki_applet = NULL;
+ VCardAppletPrivate *applet_private = NULL;
+ VCardBufferResponse *buffer_response;
+ int size, next;
+ unsigned char *sign_buffer;
+ VCard7816Status status;
+
+ applet_private = vcard_get_current_applet_private(card, apdu->a_channel);
+ ASSERT(applet_private);
+ pki_applet = &(applet_private->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(pki_applet->cert_buffer, size, 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;
+ }
+ /* if the size is too large for a single return, use the deferred
+ * buffer response */
+ if (size >= 256) {
+ 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(sign_buffer, size);
+ if (buffer_response == NULL) {
+ *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+ break;
+ }
+ *response = vcard_response_new_status_bytes(VCARD7816_SW1_RESPONSE_BYTES,
+ size & 0xff);
+ if (*response == NULL) {
+ vcard_buffer_response_delete(buffer_response);
+ *response = vcard_make_response(VCARD7816_STATUS_EXC_ERROR_MEMORY_FAILURE);
+ } else {
+ vcard_set_buffer_response(card,buffer_response);
+ }
+ break;
+ }
+ *response = vcard_response_new(sign_buffer, size, 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);
+}
+
+
+/*
+ * 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);
+
+}
+
+/* forward delcaration so the constructor can come first */
+static void cac_delete_pki_applet_data(void *applet_private);
+
+/*
+ * 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->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->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);
+ }
+}
+
+
+/*
+ * 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, 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 };
+/*
+ * 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(VCard *card, const unsigned char *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, cac_default_container_aid,
+ sizeof(cac_default_container_aid));
+ if (applet == NULL) {
+ goto failure;
+ }
+ vcard_add_applet(card, applet);
+ return VCARD_DONE;
+
+failure:
+ return VCARD_FAIL;
+}
+
+
+
diff --git a/card_7816.c b/card_7816.c
new file mode 100644
index 0000000..7ed70b7
--- /dev/null
+++ b/card_7816.c
@@ -0,0 +1,664 @@
+/*
+ * 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, VCard7816Status 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.
+ */
+static 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;
+}
+
+/*
+ * general buffer to hold results from APDU calls
+ */
+VCardResponse *
+vcard_response_new(unsigned char *buf, int len, VCard7816Status status)
+{
+ VCardResponse *new_response;
+ 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(unsigned char *buf, int len,
+ unsigned char sw1, unsigned char sw2)
+{
+ VCardResponse *new_response;
+ 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(VCard7816Status 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 VCard7816Status
+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:
+ dafult:
+ 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 VCard7816Status
+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, VCard7816Status *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(VCard7816Status 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;
+ VCard7816Status 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: /* proprietory 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);
+ *response = vcard_make_response(current_applet != NULL ?
+ VCARD7816_STATUS_SUCCESS :
+ 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(buffer_response->current, bytes_to_copy,
+ 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;
+
+ /* 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;
+ }
+
+ 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);
+ }
+ *response = vcard_make_response(VCARD7816_STATUS_ERROR_COMMAND_NOT_SUPPORTED);
+ return VCARD_DONE;
+}
diff --git a/card_7816.h b/card_7816.h
new file mode 100644
index 0000000..0047e95
--- /dev/null
+++ b/card_7816.h
@@ -0,0 +1,67 @@
+/*
+ * 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(unsigned char *buf, int len, unsigned short status);
+/* response from a return buffer and status bytes */
+VCardResponse *vcard_response_new_bytes(unsigned char *buf, int len,
+ 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(unsigned short status);
+
+
+
+/*
+ * 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);
+
+/*
+ * Constructor for a VCardApplet
+ */
+VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
+ unsigned char *aid, int aid_len);
+
+/*
+ * destructor for a VCardApplet
+ * Can be called with a NULL applet
+ */
+void vcard_delete_applet(VCardApplet *applet);
+
+
+/* add an applet to the current card */
+VCardStatus vcard_add_applet(VCard *card, VCardApplet *applet);
+
+/*
+ * 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/card_7816t.h b/card_7816t.h
new file mode 100644
index 0000000..81cb5af
--- /dev/null
+++ b/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 VCard7816Status;
+
+struct VCardResponseStruct {
+ unsigned char *b_data;
+ VCard7816Status 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) \
+const static 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) \
+const static 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 */
+ 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/event.c b/event.c
new file mode 100644
index 0000000..1d07e80
--- /dev/null
+++ b/event.c
@@ -0,0 +1,108 @@
+
+/*
+ *
+ */
+#include "vcard.h"
+#include "vreader.h"
+#include "event.h"
+
+/*
+ * OS includes
+ */
+#include <stdlib.h>
+
+/*
+ * from spice
+ */
+#include "mutex.h"
+
+Event *
+event_new(EventType type, VReader *reader, VCard *card)
+{
+ Event *new_event;
+
+ new_event = (Event *)malloc(sizeof(Event));
+ if (new_event == NULL) {
+ return NULL;
+ }
+ new_event->next = NULL;
+ new_event->type = type;
+ new_event->reader = vreader_reference(reader);
+ new_event->card = vcard_reference(card);
+
+ return new_event;
+}
+
+void
+event_delete(Event *event)
+{
+ if (event == NULL) {
+ return;
+ }
+ vreader_free(event->reader);
+ vcard_free(event->card);
+ free(event);
+}
+
+/*
+ * Event queue management
+ */
+
+static Event *event_queue_head = NULL;
+static Event *event_queue_tail = NULL;
+static mutex_t event_queue_lock;
+static condition_t event_queue_condition;
+
+void
+event_queue_init()
+{
+ MUTEX_INIT(event_queue_lock);
+ CONDITION_INIT(event_queue_condition);
+ event_queue_head = event_queue_tail = NULL;
+}
+
+void
+event_queue_event(Event *event)
+{
+ event->next = NULL;
+ MUTEX_LOCK(event_queue_lock);
+ if (event_queue_head) {
+ assert(event_queue_tail);
+ event_queue_tail->next = event;
+ } else {
+ event_queue_head = event;
+ }
+ event_queue_tail = event;
+ CONDITION_NOTIFY(event_queue_condition);
+ MUTEX_UNLOCK(event_queue_lock);
+}
+
+/* must have lock */
+static Event *
+event_dequeue_event(void)
+{
+ Event *event = NULL;
+ if (event_queue_head) {
+ event = event_queue_head;
+ event_queue_head = event->next;
+ event->next = NULL;
+ }
+ return event;
+}
+
+Event *
+event_wait_next_event()
+{
+ Event *event;
+
+ MUTEX_LOCK(event_queue_lock);
+ while ((event = event_dequeue_event()) == NULL) {
+ CONDITION_WAIT(event_queue_condition, event_queue_lock);
+ }
+ MUTEX_UNLOCK(event_queue_lock);
+ return event;
+}
+
+
+
+
diff --git a/event.h b/event.h
new file mode 100644
index 0000000..8aa301a
--- /dev/null
+++ b/event.h
@@ -0,0 +1,20 @@
+/*
+ *
+ */
+#ifndef EVENT_H
+#define EVENT_H 1
+#include "eventt.h"
+#include "vreadert.h"
+#include "vcardt.h"
+
+Event *event_new(EventType type, VReader *reader, VCard *card);
+void event_delete(Event *);
+
+/*
+ * Event queueing services
+ */
+void event_queue_event(Event *);
+void event_queue_init();
+Event *event_wait_next_event();
+
+#endif
diff --git a/eventt.h b/eventt.h
new file mode 100644
index 0000000..82dbdac
--- /dev/null
+++ b/eventt.h
@@ -0,0 +1,27 @@
+/*
+ *
+ */
+
+#ifndef EVENTT_H
+#define EVENTT_H 1
+#include "vreadert.h"
+#include "vcardt.h"
+
+typedef struct EventStruct Event;
+
+typedef enum {
+ EVENT_READER_INSERT,
+ EVENT_READER_REMOVE,
+ EVENT_CARD_INSERT,
+ EVENT_CARD_REMOVE
+} EventType;
+
+struct EventStruct {
+ Event *next;
+ EventType type;
+ VReader *reader;
+ VCard *card;
+};
+#endif
+
+
diff --git a/link_test.c b/link_test.c
new file mode 100644
index 0000000..4d45d3e
--- /dev/null
+++ b/link_test.c
@@ -0,0 +1,18 @@
+/*
+ *
+ */
+#include <stdio.h>
+#include "vcard.h"
+
+VCardStatus cac_card_init(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/mutex.h b/mutex.h
new file mode 100644
index 0000000..1139671
--- /dev/null
+++ b/mutex.h
@@ -0,0 +1,43 @@
+/* -*- 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/>.
+*/
+
+#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)
+#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)
+#endif
+
+#endif // _H_MUTEX
diff --git a/scard_common.h b/scard_common.h
new file mode 100644
index 0000000..8e54f86
--- /dev/null
+++ b/scard_common.h
@@ -0,0 +1,16 @@
+#ifndef _SCARD_COMMON_H
+#define _SCARD_COMMON_H
+
+typedef enum {
+ SCard_ATR,
+ SCard_APDU,
+ SCard_Remove,
+ SCard_Error
+} MsgType;
+
+typedef struct SCRMsgHeader {
+ MsgType type;
+ unsigned int nLength;
+} SCRMsgHeader;
+
+#endif
diff --git a/vcard.c b/vcard.c
new file mode 100644
index 0000000..0ecbd12
--- /dev/null
+++ b/vcard.c
@@ -0,0 +1,289 @@
+/*
+ * implement the Java card standard.
+ *
+ */
+#include "vcard.h"
+#include "card_7816t.h"
+#include <stdlib.h>
+#include <string.h>
+
+struct VCardAppletStruct {
+ VCardApplet *next;
+ VCardProcessAPDU process_apdu;
+ 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;
+};
+
+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)
+{
+ card->current_applet[0] = NULL;
+ card->current_applet[1] = NULL;
+ card->current_applet[2] = NULL;
+ card->current_applet[3] = NULL;
+ if (card->vcard_buffer_response) {
+ vcard_buffer_response_delete(card->vcard_buffer_response);
+ card->vcard_buffer_response = NULL;
+ }
+ vcard_emul_reset(card, power);
+}
+
+/* applet utilities */
+
+/*
+ * applet utilities
+ */
+/* constructor */
+VCardApplet *
+vcard_new_applet(VCardProcessAPDU applet_process_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->aid = (char *)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->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;
+}
+
+
+VCardStatus
+vcard_add_applet(VCard *card, VCardApplet *applet)
+{
+ applet->next = card->applet_list;
+ card->applet_list = 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;
+}
+
+void
+vcard_select_applet(VCard *card, int channel, VCardApplet *applet)
+{
+ ASSERT(channel < MAX_CHANNEL);
+ card->current_applet[channel] = applet;
+}
+
+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/vcard.h b/vcard.h
new file mode 100644
index 0000000..e83b677
--- /dev/null
+++ b/vcard.h
@@ -0,0 +1,68 @@
+/*
+ *
+ */
+#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
+ */
+/* constuctor */
+VCardApplet *vcard_new_applet(VCardProcessAPDU applet_process_function,
+ unsigned char *aid, int aid_len);
+/* destructor */
+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);
+
+/*
+ * 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);
+
+/* 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 *);
+
+/* 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/vcard_emul.h b/vcard_emul.h
new file mode 100644
index 0000000..c3465a3
--- /dev/null
+++ b/vcard_emul.h
@@ -0,0 +1,67 @@
+/*
+ * 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"
+
+/*
+ * types
+ */
+typedef enum {
+ VCARD_EMUL_NONE =0,
+ VCARD_EMUL_CAC
+} VCardEmulType;
+
+typedef enum {
+ VCARD_EMUL_OK =0,
+ VCARD_EMUL_FAIL
+} VCardEmulError;
+
+typedef struct VirtualReaderOptionsStruct VirtualReaderOptions;
+
+struct VirtualReaderOptionsStruct {
+ char *name;
+ VCardEmulType card_type;
+ char **cert_name;
+ int cert_count;
+};
+
+typedef struct VCardEmulOptionsStruct VCardEmulOptions;
+struct VCardEmulOptionsStruct {
+ void *nss_db;
+ VirtualReaderOptions *vreader;
+ int vreader_count;
+ VCardEmulType hw_card_type;
+};
+
+/*
+ * 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) */
+VCard7816Status 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' */
+VCard7816Status 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);
+
+VCardEmulError vcard_emul_init(const VCardEmulOptions *options);
+#endif
diff --git a/vcard_emul_nss.c b/vcard_emul_nss.c
new file mode 100644
index 0000000..d4f5785
--- /dev/null
+++ b/vcard_emul_nss.c
@@ -0,0 +1,756 @@
+/*
+ * 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 "event.h"
+
+/*
+ * NSS headers
+ */
+#include <nss.h>
+#include <pk11pub.h>
+#include <cert.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;
+};
+
+struct VReaderEmulStruct {
+ PK11SlotInfo *slot;
+ VCardEmulType default_type;
+ PRBool present;
+ int series;
+ VCard *saved_vcard;
+};
+
+/* if we have mor 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_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 (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 VCard7816Status
+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' */
+VCard7816Status
+vcard_emul_rsa_op(VCard *card, VCardKey *key, unsigned char *buffer, int buffer_size)
+{
+ SECKEYPrivateKey *priv_key = vcard_emul_get_nss_key(key);
+ int signature_len;
+ SECStatus rv;
+
+ if (key == NULL) {
+ /* couldn't get the key, indicate that we aren't logged in */
+ return VCARD7816_STATUS_ERROR_CONDITION_NOT_SATISFIED;
+ }
+
+ /*
+ * 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) */
+VCard7816Status
+vcard_emul_login(VCard *card, unsigned char *pin, int pin_len)
+{
+ PK11SlotInfo *slot = vcard_emul_get_slot(card);
+ char *pin_string = NULL;
+ SECStatus rv;
+
+ /* 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 = (char *)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;
+
+ 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 = vcard_emul_get_slot(card);
+ /* 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? */
+ (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)
+{
+ 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->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);
+ }
+ free(vreader_emul);
+}
+
+/*
+ * TODO: move this to emulater non-specific directory
+ */
+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;
+ }
+
+ /* look at the actual card. Right now we only know about CAC, so return CAC */
+ return VCARD_EMUL_CAC;
+}
+
+/*
+ * 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
+ */
+const static unsigned char nss_atr[] = { VCARD_ATR_PREFIX, '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(PK11SlotInfo *slot, VCardEmulType type,
+ unsigned char **certs, int *cert_len, VCardKey **keys, int cert_count)
+{
+ VCardEmul *vcard_emul;
+ VCard *vcard;
+
+ /* ingnore the inserted card */
+ if (type == VCARD_EMUL_NONE) {
+ return 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;
+ }
+ /* here is where we would select the card type we want to emulate. for now we only have
+ * cac cards */
+ ASSERT(type == VCARD_EMUL_CAC);
+ cac_card_init(vcard, certs, cert_len, keys, cert_count);
+ return vcard;
+}
+
+
+/*
+ * 'clone' a physical card as a virtual card
+ */
+static VCard *
+vcard_emul_mirror_card(PK11SlotInfo *slot, VCardEmulType type)
+{
+ /*
+ * 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;
+ PRBool ret;
+
+
+ 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(slot, type, certs, cert_len, keys, cert_count);
+}
+
+static VCardEmulType default_card_type = VCARD_EMUL_NONE;
+
+/*
+ * 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);
+ vreader = vreader_new(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) {
+ VCardEmulType type;
+ if (vreader_emul->present) {
+ vreader_insert_card(vreader, NULL);
+ }
+ type = vcard_emul_get_type(vreader);
+ vcard = vcard_emul_mirror_card(slot, type);
+ 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 = { NULL, NULL, 0, VCARD_EMUL_CAC };
+
+/*
+ * 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 (!vreader_card_is_present(vreader)) {
+ 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 = vreader_get_private(vreader);
+ VCard *vcard;
+
+ if (vreader_card_is_present(vreader)) {
+ return VCARD_EMUL_FAIL; /* card is already inserted */
+ }
+
+ /* 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_emul->slot, vcard_emul_get_type(vreader));
+ }
+ vreader_insert_card(vreader, vcard);
+ vcard_free(vcard);
+
+ return VCARD_EMUL_OK;
+}
+
+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();
+ event_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);
+ vreader = vreader_new(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(slot, options->vreader[i].card_type, 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;
+ }
+ }
+
+ /* 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("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;
+
+ 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);
+ vreader = vreader_new(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(slot, type);
+ 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);
+
+ return has_readers ? VCARD_EMUL_OK : VCARD_EMUL_FAIL;
+}
diff --git a/vcardt.h b/vcardt.h
new file mode 100644
index 0000000..80a6ba0
--- /dev/null
+++ b/vcardt.h
@@ -0,0 +1,56 @@
+/*
+ *
+ */
+#ifndef VCARDT_H
+#define VCARDT_H 1
+/*
+ * these should come from some common spice header file
+ */
+#include <assert.h>
+#define ASSERT assert
+#define MIN(x,y) ((x)>(y)?(y):(x))
+#define MAX(x,y) ((x)>(y)?(x):(y))
+
+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 0x3b, 0x6f, 0x00, 0xff, 'V', 'C', 'A', 'R', 'D', '_'
+
+typedef enum {
+ VCARD_DONE,
+ VCARD_NEXT,
+ VCARD_FAIL
+} VCardStatus;
+
+typedef enum {
+ VCARD_FILE_SYSTEM,
+ VCARD_VM
+} VCardType;
+
+typedef enum {
+ VCARD_POWER_ON,
+ VCARD_POWER_OFF
+} VCardPower;
+
+typedef VCardStatus (*VCardProcessAPDU)(VCard *card, VCardAPDU *apdu,
+ VCardResponse **response);
+typedef void (*VCardAppletPrivateFree) (VCardAppletPrivate *);
+typedef void (*VCardEmulFree) (VCardEmul *);
+
+struct VCardBufferResponseStruct {
+ unsigned char *buffer;
+ int buffer_len;
+ unsigned char *current;
+ int len;
+};
+
+#endif
diff --git a/vreader.c b/vreader.c
new file mode 100644
index 0000000..0fa1c23
--- /dev/null
+++ b/vreader.c
@@ -0,0 +1,444 @@
+/*
+ * emulate the reader
+ */
+#include "vcard.h"
+#include "vcard_emul.h"
+#include "card_7816.h"
+#include "vreader.h"
+#include "event.h"
+
+/*
+ * System includes
+ */
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * spice includes
+ */
+#include "mutex.h"
+
+struct VReaderStruct {
+ int reference_count;
+ VCard *card;
+ 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(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->card = NULL;
+ 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->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;
+}
+
+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_emul_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)
+{
+ VReaderStatus status;
+
+ 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;
+}
+
+void
+vreader_list_entry_delete(VReaderListEntry *entry)
+{
+ if (entry == NULL) {
+ return;
+ }
+ vreader_free(entry->reader);
+ free(entry);
+}
+
+
+static VReaderList *
+vreader_list_new()
+{
+ 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;
+
+void
+vreader_list_init()
+{
+ vreader_list = vreader_list_new();
+ MUTEX_INIT(vreader_list_mutex);
+}
+
+static void
+vreader_list_lock()
+{
+ MUTEX_LOCK(vreader_list_mutex);
+}
+
+static void
+vreader_list_unlock()
+{
+ 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);
+ }
+ vreader_list_unlock();
+ return new_list;
+}
+
+VReaderList *
+vreader_get_reader_list()
+{
+ VReaderList *new_reader_list;
+
+ vreader_list_lock();
+ new_reader_list = vreader_copy_list(vreader_list);
+ vreader_list_unlock();
+ return new_reader_list;
+}
+
+/* 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();
+ event_queue_event(event_new(EVENT_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);
+ event_queue_event(event_new(EVENT_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);
+ event_queue_event(event_new(card ? EVENT_CARD_INSERT : EVENT_CARD_REMOVE, reader, card));
+ return VREADER_OK;
+}
+
+/*
+ * initialize all the static reader structures
+ */
+void
+vreader_init()
+{
+ vreader_list_init();
+}
+
+
+
+
+
+
+
+
diff --git a/vreader.h b/vreader.h
new file mode 100644
index 0000000..58f6da2
--- /dev/null
+++ b/vreader.h
@@ -0,0 +1,45 @@
+/*
+ *
+ */
+
+#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);
+VReaderList *vreader_get_reader_list();
+
+/* constructor */
+VReader *vreader_new(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);
+VReaderEmul *vreader_get_private(VReader *);
+VReaderStatus vreader_card_is_present(VReader *reader);
+
+/* list operations */
+VReaderList *vreader_get_reader_list();
+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);
+
+/*
+ * list tools for vcard_emul
+ */
+void vreader_init();
+VReaderStatus vreader_add_reader(VReader *reader);
+VReaderStatus vreader_remove_reader(VReader *reader);
+VReaderStatus vreader_insert_card(VReader *reader, VCard *card);
+
+#endif
diff --git a/vreadert.h b/vreadert.h
new file mode 100644
index 0000000..5a66b99
--- /dev/null
+++ b/vreadert.h
@@ -0,0 +1,22 @@
+/*
+ *
+ */
+
+#ifndef VREADERT_H
+#define VREADERT_H 1
+
+typedef enum {
+ VREADER_OK=0,
+ VREADER_NO_CARD,
+ VREADER_OUT_OF_MEMORY
+} VReaderStatus;
+
+typedef struct VReaderStruct VReader;
+typedef struct VReaderListStruct VReaderList;
+typedef struct VReaderListEntryStruct VReaderListEntry;
+
+typedef struct VReaderEmulStruct VReaderEmul;
+typedef void (*VReaderEmulFree)(VReaderEmul *);
+
+#endif
+
diff --git a/vscclient.c b/vscclient.c
new file mode 100644
index 0000000..815a9dc
--- /dev/null
+++ b/vscclient.c
@@ -0,0 +1,275 @@
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "scard_common.h"
+
+#include "vreader.h"
+#include "vcard_emul.h"
+#include "event.h"
+
+#include "mutex.h"
+
+
+typedef unsigned char BYTE;
+typedef unsigned long LONG;
+
+int verbose = 0;
+
+
+int sock;
+
+int
+PrintByteArray (
+ BYTE *arrBytes,
+ unsigned int nSize
+) {
+ int i;
+ for (i=0; i < nSize; i++) {
+ printf ("%X ", arrBytes[i]);
+ }
+ printf ("\n");
+}
+
+void
+PrintUsage () {
+ printf ("vscclient <host> <port> \n");
+}
+
+static mutex_t write_lock;
+
+int
+SendMsg (
+ MsgType type,
+ void *msg,
+ unsigned int nLength
+) {
+ LONG rv;
+ SCRMsgHeader mhHeader;
+
+ MUTEX_LOCK(write_lock);
+
+ mhHeader.type = type;
+ mhHeader.nLength = nLength;
+ 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,
+ nLength
+ );
+ if (rv < 0) {
+ /* Error */
+ printf ("write error\n");
+ close (sock);
+ MUTEX_UNLOCK(write_lock);
+ return (16);
+ }
+ MUTEX_UNLOCK(write_lock);
+
+ return (0);
+}
+
+
+#define MAX_ATR_LEN 40
+static void *
+event_thread(void *arg)
+{
+ VReader *reader = (VReader *)arg;
+ unsigned char atr[ MAX_ATR_LEN];
+ int atr_len = MAX_ATR_LEN;
+ Event *event = NULL;
+
+
+ while (1) {
+ event = event_wait_next_event();
+ if (event == NULL) {
+ break;
+ }
+ if (event->reader != reader) {
+ event_delete(event);
+ continue;
+ }
+ switch (event->type) {
+ case EVENT_READER_INSERT:
+ /* future, tell qemu to insert a new CCID reader */
+ break;
+ case EVENT_READER_REMOVE:
+ /* future, tell qemu that an old CCID reader has been removed */
+ break;
+ case EVENT_CARD_INSERT:
+ /* get the ATR (intended as a response to a power on from the reader */
+ vreader_power_on(reader, atr, &atr_len);
+ /* ATR call functions as a Card Insert event */
+ SendMsg (
+ SCard_ATR,
+ atr,
+ atr_len
+ );
+ break;
+ case EVENT_CARD_REMOVE:
+ // Card removed
+ SendMsg (
+ SCard_Remove,
+ NULL,
+ 0
+ );
+ break;
+ }
+ event_delete(event);
+ }
+ return NULL;
+}
+
+#define APDUBufSize 270
+
+int
+main (
+ int argc,
+ char *argv[]
+) {
+ struct sockaddr_in servaddr;
+
+ SCRMsgHeader mhHeader;
+
+ LONG rv;
+ int dwSendLength;
+ int dwRecvLength;
+ BYTE pbRecvBuffer[APDUBufSize];
+ BYTE pbSendBuffer[APDUBufSize];
+ VReaderStatus reader_status;
+ VReaderList *reader_list;
+ VReader *reader;
+ pthread_t thread_id;
+
+
+ if (argc != 3) {
+ PrintUsage();
+ exit (4);
+ }
+
+ sock = socket (
+ AF_INET,
+ SOCK_STREAM,
+ 0
+ );
+ if (sock < 0) {
+ // Error
+ printf ("Error opening socket!\n");
+ }
+
+ memset (&servaddr, 0, sizeof (struct sockaddr_in));
+ servaddr.sin_family = AF_INET;
+ servaddr.sin_addr.s_addr = inet_addr (argv[1]);
+ servaddr.sin_port = htons (atoi(argv[2]));
+
+ if (
+ connect (
+ sock,
+ (struct sockaddr *)&servaddr,
+ sizeof(servaddr)
+ ) < 0
+ ) {
+ // Error
+ printf ("Could not connect\n");
+ return (5);
+ }
+if (verbose)
+ printf ("Connected (sizeof Header=%d)!\n", sizeof (mhHeader));
+
+ MUTEX_INIT(write_lock);
+
+ vcard_emul_init(NULL); /* take the defaults for now; */
+
+ /*
+ * the emulator can handle more than one reader at once, right now this test program
+ * can only handle a single reader.
+ */
+ reader_list = vreader_get_reader_list();
+ reader = vreader_list_get_reader(vreader_list_get_first(reader_list));
+
+ rv = pthread_create(&thread_id, NULL, event_thread, reader);
+ if (rv < 0) {
+ perror("pthread_create");
+ exit (1);
+ }
+
+ do {
+ rv = read (
+ sock,
+ &mhHeader,
+ sizeof (mhHeader)
+ );
+ if (rv < 0) {
+ /* Error */
+ printf ("header read error\n");
+ return (8);
+ }
+if (verbose)
+ printf ("Header: type=%d, nLength=%d\n", mhHeader.type, mhHeader.nLength);
+ switch (mhHeader.type) {
+ case SCard_APDU:
+ rv = read (
+ sock,
+ pbSendBuffer,
+ mhHeader.nLength
+ );
+ if (rv < 0) {
+ /* Error */
+ printf ("read error\n");
+ close (sock);
+ return (8);
+ }
+if (verbose) {
+ printf (" recv APDU: ");
+ PrintByteArray (pbSendBuffer, mhHeader.nLength);
+}
+ /* Transmit recieved APDU */
+ dwSendLength = mhHeader.nLength;
+ dwRecvLength = sizeof(pbRecvBuffer);
+ reader_status = vreader_xfr_bytes(reader,
+ pbSendBuffer, dwSendLength,
+ pbRecvBuffer, &dwRecvLength);
+ if (reader_status == VREADER_OK) {
+ mhHeader.nLength = dwRecvLength;
+if (verbose) {
+ printf (" send APDU: ");
+ PrintByteArray (pbRecvBuffer, mhHeader.nLength);
+}
+ SendMsg (
+ SCard_APDU,
+ pbRecvBuffer,
+ dwRecvLength
+ );
+ } else {
+ rv = reader_status; /* warning: not meaningful */
+ SendMsg (
+ SCard_Error,
+ &rv,
+ sizeof (LONG)
+ );
+ }
+ break;
+ default:
+ printf ("Default\n");
+ }
+ } while (rv >= 0);
+
+
+ return (0);
+}