[pkg-opensc-commit] [opensc] 179/295: adding a CAC support into OpenSC (#841)

Eric Dorland eric at moszumanska.debian.org
Sat Jun 24 21:11:29 UTC 2017


This is an automated email from the git hooks/post-receive script.

eric pushed a commit to branch master
in repository opensc.

commit 777e2a3751e3f6d53f056c98e9e20e42af674fb1
Author: Jakuje <jakuje at gmail.com>
Date:   Mon Feb 27 11:05:12 2017 +0100

    adding a CAC support into OpenSC (#841)
    
    * Includes adding support for parsing extensions from a certificate.
    * Move lebytes2ushort() to related functions in internals.h
    * Adds Simple TLV related functions
---
 src/common/simclist.h       |    2 +-
 src/libopensc/Makefile.am   |   12 +-
 src/libopensc/Makefile.mak  |   10 +-
 src/libopensc/card-cac.c    | 1555 +++++++++++++++++++++++++++++++++++++++++++
 src/libopensc/cardctl.h     |   11 +
 src/libopensc/cards.h       |    8 +-
 src/libopensc/ctx.c         |    1 +
 src/libopensc/internal.h    |    7 +
 src/libopensc/pkcs15-cac.c  |  436 ++++++++++++
 src/libopensc/pkcs15-cert.c |    5 +-
 src/libopensc/pkcs15-syn.c  |    1 +
 src/libopensc/pkcs15-syn.h  |    1 +
 src/libopensc/sc.c          |    7 +
 src/libopensc/simpletlv.c   |   99 +++
 src/libopensc/simpletlv.h   |   54 ++
 15 files changed, 2196 insertions(+), 13 deletions(-)

diff --git a/src/common/simclist.h b/src/common/simclist.h
index 3fd5254..49ac31f 100644
--- a/src/common/simclist.h
+++ b/src/common/simclist.h
@@ -124,7 +124,7 @@ typedef int (*element_comparator)(const void *a, const void *b);
 typedef int (*element_seeker)(const void *el, const void *indicator);
 
 /**
- * an element lenght meter.
+ * an element length meter.
  *
  * An element meter is a function that:
  *      -# receives the reference to an element el
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index a31d5e6..4f39f65 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -8,7 +8,7 @@ lib_LTLIBRARIES = libopensc.la
 noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \
 	internal-winscard.h p15card-helper.h pkcs15-syn.h \
 	opensc.h pkcs15.h \
-	cardctl.h asn1.h log.h \
+	cardctl.h asn1.h log.h simpletlv.h \
 	errors.h types.h compression.h itacns.h iso7816.h \
 	authentic.h iasecc.h iasecc-sdo.h sm.h card-sc-hsm.h \
 	pace.h cwa14890.h cwa-dnie.h card-gids.h aux-data.h \
@@ -22,6 +22,7 @@ AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \
 libopensc_la_SOURCES = \
 	sc.c ctx.c log.c errors.c \
 	asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c padding.c apdu.c \
+	simpletlv.c \
 	\
 	pkcs15.c pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \
 	pkcs15-prkey.c pkcs15-pubkey.c pkcs15-skey.c \
@@ -35,8 +36,8 @@ libopensc_la_SOURCES = \
 	card-cardos.c card-tcos.c card-default.c \
 	card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \
 	card-oberthur.c card-belpic.c card-atrust-acos.c \
-	card-entersafe.c card-epass2003.c card-coolkey.c \
-	card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
+	card-entersafe.c card-epass2003.c card-coolkey.c card-incrypto34.c \
+	card-piv.c card-cac.c card-muscle.c card-acos5.c \
 	card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
 	card-rtecp.c card-westcos.c card-myeid.c \
 	card-itacns.c card-authentic.c \
@@ -47,8 +48,9 @@ libopensc_la_SOURCES = \
 	pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
 	pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
 	pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
-	pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \
-	pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c pkcs15-coolkey.c \
+	pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \
+	pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \
+	pkcs15-coolkey.c \
 	pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c \
 	compression.c p15card-helper.c sm.c \
 	aux-data.c \
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 741be19..e369ec6 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -3,7 +3,8 @@ TOPDIR = ..\..
 TARGET                  = opensc.dll opensc_a.lib
 OBJECTS			= \
 	sc.obj ctx.obj log.obj errors.obj \
-	asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj padding.obj apdu.obj \
+	asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj \
+	padding.obj apdu.obj simpletlv.obj \
 	\
 	pkcs15.obj pkcs15-cert.obj pkcs15-data.obj pkcs15-pin.obj \
 	pkcs15-prkey.obj pkcs15-pubkey.obj pkcs15-skey.obj \
@@ -18,7 +19,8 @@ OBJECTS			= \
 	card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \
 	card-oberthur.obj card-belpic.obj card-atrust-acos.obj \
 	card-entersafe.obj card-epass2003.obj card-coolkey.obj \
-	card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \
+	card-incrypto34.obj card-cac.obj card-piv.obj card-muscle.obj \
+	card-acos5.obj \
 	card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
 	card-rtecp.obj card-westcos.obj card-myeid.obj \
 	card-itacns.obj card-authentic.obj \
@@ -29,8 +31,8 @@ OBJECTS			= \
 	pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
 	pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
 	pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
-	pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \
-	pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
+	pkcs15-cac.obj pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj \
+	pkcs15-oberthur.obj pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
 	pkcs15-dnie.obj pkcs15-gids.obj pkcs15-iasecc.obj pkcs15-jpki.obj \
 	compression.obj p15card-helper.obj sm.obj \
 	aux-data.obj \
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
new file mode 100644
index 0000000..d5f8585
--- /dev/null
+++ b/src/libopensc/card-cac.c
@@ -0,0 +1,1555 @@
+/*
+ * card-cac.c: Support for CAC from NIST SP800-73
+ * card-default.c: Support for cards with no driver
+ *
+ * Copyright (C) 2001, 2002  Juha Yrjölä <juha.yrjola at iki.fi>
+ * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert at anl.gov>
+ * Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning at identityalliance.com>
+ * Copyright (C) 2007, EMC, Russell Larner <rlarner at rsa.com>
+ * Copyright (C) 2016, Red Hat, Inc.
+ *
+ * CAC driver author: Robert Relyea <rrelyea at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifdef ENABLE_OPENSSL
+	/* openssl only needed for card administration */
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#endif /* ENABLE_OPENSSL */
+
+#include "internal.h"
+#include "simpletlv.h"
+#include "cardctl.h"
+#ifdef ENABLE_ZLIB
+#include "compression.h"
+#endif
+#include "iso7816.h"
+
+#define CAC_MAX_SIZE 4096		/* arbitrary, just needs to be 'large enough' */
+/*
+ *  CAC hardware and APDU constants
+ */
+#define CAC_MAX_CHUNK_SIZE 240
+#define CAC_INS_GET_CERTIFICATE 0x36  /* CAC1 command to read a certificate */
+#define CAC_INS_SIGN_DECRYPT    0x42  /* A crypto operation */
+#define CAC_P1_STEP    0x80
+#define CAC_P1_FINAL   0x00
+#define CAC_INS_READ_FILE       0x52  /* read a TL or V file */
+#define CAC_FILE_TAG    1
+#define CAC_FILE_VALUE  2
+/* TAGS in a TL file */
+#define CAC_TAG_CERTIFICATE           0x70
+#define CAC_TAG_CERTINFO              0x71
+#define CAC_TAG_CUID                  0xF0
+#define CAC_TAG_CC_VERSION_NUMBER     0xF1
+#define CAC_TAG_GRAMMAR_VERION_NUMBER 0xF2
+#define CAC_TAG_CARDURL               0xF3
+#define CAC_TAG_PKCS15                0xF4
+#define CAC_TAG_ACCESS_CONTROL        0xF6
+#define CAC_TAG_DATA_MODEL            0xF5
+#define CAC_TAG_CARD_APDU             0xF7
+#define CAC_TAG_REDIRECTION           0xFA
+#define CAC_TAG_CAPABILITY_TUPLES     0xFB
+#define CAC_TAG_STATUS_TUPLES         0xFC
+#define CAC_TAG_NEXT_CCC              0xFD
+#define CAC_TAG_ERROR_CODES           0xFE
+#define CAC_APP_TYPE_GENERAL    0x01
+#define CAC_APP_TYPE_SKI        0x02
+#define CAC_APP_TYPE_PKI        0x04
+
+/* hardware data structures (returned in the CCC) */
+/* part of the card_url */
+typedef struct cac_access_profile {
+	u8 GCACR_listID;
+	u8 GCACR_readTagListACRID;
+	u8 GCACR_updatevalueACRID;
+	u8 GCACR_readvalueACRID;
+	u8 GCACR_createACRID;
+	u8 GCACR_deleteACRID;
+	u8 CryptoACR_listID;
+	u8 CryptoACR_getChallengeACRID;
+	u8 CryptoACR_internalAuthenicateACRID;
+	u8 CryptoACR_pkiComputeACRID;
+	u8 CryptoACR_readTagListACRID;
+	u8 CryptoACR_updatevalueACRID;
+	u8 CryptoACR_readvalueACRID;
+	u8 CryptoACR_createACRID;
+	u8 CryptoACR_deleteACRID;
+} cac_access_profile_t;
+
+/* part of the card url */
+typedef struct cac_access_key_info {
+	u8	keyFileID[2];
+	u8	keynumber;
+} cac_access_key_info_t;
+
+typedef struct cac_card_url {
+	u8 rid[5];
+	u8 cardApplicationType;
+	u8 objectID[2];
+	u8 applicationID[2];
+	cac_access_profile_t accessProfile;
+	u8 pinID;			     /* not used for VM cards */
+	cac_access_key_info_t accessKeyInfo; /* not used for VM cards */
+	u8 keyCryptoAlgorithm;               /* not used for VM cards */
+} cac_card_url_t;
+
+typedef struct cac_cuid {
+	u8 gsc_rid[5];
+	u8 manufacturer_id;
+	u8 card_type;
+	u8 card_id;
+} cac_cuid_t;
+
+/* data structures to store meta data about CAC objects */
+typedef struct cac_object {
+	const char *name;
+	int fd;
+	sc_path_t path;
+} cac_object_t;
+
+/*
+ * Flags for Current Selected Object Type
+ *   CAC files are TLV files, with TL and V separated. For generic
+ *   containers we reintegrate the TL anv V portions into a single
+ *   file to read. Certs are also TLV files, but pkcs15 wants the
+ *   actual certificate. At select time we know the patch which tells
+ *   us what time of files we want to read. We remember that type
+ *   so that read_binary can do the appropriate processing.
+ */
+#define CAC_OBJECT_TYPE_CERT		1
+#define CAC_OBJECT_TYPE_TLV_FILE	4
+
+/*
+ * CAC private data per card state
+ */
+typedef struct cac_private_data {
+	int object_type;		/* select set this so we know how to read the file */
+	int cert_next;			/* index number for the next certificate found in the list */
+	u8 *cache_buf;			/* cached version of the currently selected file */
+	size_t cache_buf_len;		/* length of the cached selected file */
+	int cached;			/* is the cached selected file valid */
+	cac_cuid_t cuid;                /* card unique ID from the CCC */
+	u8 *cac_id;                     /* card serial number */
+	size_t cac_id_len;              /* card serial number len */
+	list_t pki_list;                /* list of pki containers */
+	cac_object_t *pki_current;      /* current pki object _ctl function */
+	list_t general_list;            /* list of general containers */
+	cac_object_t *general_current;  /* current object for _ctl function */
+} cac_private_data_t;
+
+#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
+
+int cac_list_compare_path(const void *a, const void *b)
+{
+	if (a == NULL || b == NULL)
+		return 1;
+	return memcmp( &((cac_object_t *) a)->path,
+		&((cac_object_t *) b)->path, sizeof(sc_path_t));
+}
+
+/* For SimCList autocopy, we need to know the size of the data elements */
+size_t cac_list_meter(const void *el) {
+	return sizeof(cac_object_t);
+}
+
+static cac_private_data_t *cac_new_private_data(void)
+{
+	cac_private_data_t *priv;
+	priv = calloc(1, sizeof(cac_private_data_t));
+	list_init(&priv->pki_list);
+	list_attributes_comparator(&priv->pki_list, cac_list_compare_path);
+	list_attributes_copy(&priv->pki_list, cac_list_meter, 1);
+	list_init(&priv->general_list);
+	list_attributes_comparator(&priv->general_list, cac_list_compare_path);
+	list_attributes_copy(&priv->general_list, cac_list_meter, 1);
+	/* set other fields as appropriate */
+
+	return priv;
+}
+
+static void cac_free_private_data(cac_private_data_t *priv)
+{
+	free(priv->cac_id);
+	free(priv->cache_buf);
+	list_destroy(&priv->pki_list);
+	list_destroy(&priv->general_list);
+	free(priv);
+	return;
+}
+
+static int cac_add_object_to_list(list_t *list, const cac_object_t *object)
+{
+	if (list_append(list, object) < 0)
+		return SC_ERROR_UNKNOWN;
+	return SC_SUCCESS;
+}
+
+/*
+ * Set up the normal CAC paths
+ */
+#define CAC_TO_AID(x) x, sizeof(x)-1
+
+#define CAC_2_RID "\xA0\x00\x00\x01\x16"
+#define CAC_1_RID "\xA0\x00\x00\x00\x79"
+#define CAC_1_CM_AID "\xA0\x00\x00\x00\x30\x00\00"
+
+static const sc_path_t cac_CCC_Path = {
+	"", 0,
+	0,0,SC_PATH_TYPE_DF_NAME,
+	{ CAC_TO_AID(CAC_2_RID "\xDB\x00") }
+};
+
+#define MAX_CAC_SLOTS 10		/* arbitrary, just needs to be 'large enough' */
+/* default certificate labels for the CAC card */
+static const char *cac_labels[MAX_CAC_SLOTS] = {
+	"CAC ID Certificate",
+	"CAC Email Signature Certificate",
+	"CAC Email Encryption Certificate",
+	"CAC Cert 3",
+	"CAC Cert 4",
+	"CAC Cert 5",
+	"CAC Cert 6",
+	"CAC Cert 7",
+	"CAC Cert 8",
+	"CAC Cert 9"
+};
+
+/* template for a cac1 pki object */
+static const cac_object_t cac_cac1_pki_obj = {
+	"CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+	{ CAC_TO_AID(CAC_1_RID "\x01\x00") } }
+};
+
+/* template for cac1 cuid */
+static const cac_cuid_t cac_cac1_cuid = {
+	{ 0xa0, 0x00, 0x00, 0x00, 0x79 },
+	2, 2, 0
+};
+
+/*
+ *  CAC-1 general objectes defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0.
+ *   doubles as a source for CAC-2 labels.
+ */
+static const cac_object_t cac_1_objects[] = {
+	{ "Person Instance", 0x200, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\x00") }}},
+	{ "Personnel", 0x201, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\x01") }}},
+	{ "Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\x02") }}},
+	{ "Other Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\x02") }}},
+	{ "PKI Credential", 0x2FD, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\xFD") }}},
+	{ "PKI Certificate", 0x2FE, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ CAC_TO_AID(CAC_1_RID "\x02\xFE") }}},
+};
+
+static const int cac_1_object_count = sizeof(cac_1_objects)/sizeof(cac_1_objects[0]);
+
+
+/*
+ * use the object id to find our object info on the object in our CAC-1 list
+ */
+static const cac_object_t *cac_find_obj_by_id(unsigned short object_id)
+{
+	int i;
+
+	for (i=0; i < cac_1_object_count; i++) {
+		if (cac_1_objects[i].fd == object_id) {
+			return &cac_1_objects[i];
+		}
+	}
+	return NULL;
+}
+
+/*
+ * Lookup the path in the pki list to see if it is a cert path
+ */
+static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path)
+{
+	cac_object_t test_obj;
+	test_obj.path = *in_path;
+	test_obj.path.index = 0;
+	test_obj.path.count = 0;
+
+	return (list_contains(&priv->pki_list, &test_obj) != 0);
+}
+
+/*
+ * Send a command and receive data.
+ *
+ * A caller may provide a buffer, and length to read. If not provided,
+ * an internal 4096 byte buffer is used, and a copy is returned to the
+ * caller. that need to be freed by the caller.
+ *
+ * modelled after a similiar function in card-piv.c
+ */
+
+static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2,
+	const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf,
+	size_t * recvbuflen)
+{
+	int r;
+	sc_apdu_t apdu;
+	u8 rbufinitbuf[CAC_MAX_SIZE];
+	u8 *rbuf;
+	size_t rbuflen;
+
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%02x %02x %02x %d : %d %d\n",
+		 ins, p1, p2, sendbuflen, card->max_send_size, card->max_recv_size);
+
+	rbuf = rbufinitbuf;
+	rbuflen = sizeof(rbufinitbuf);
+
+	/* if caller provided a buffer and length */
+	if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) {
+		rbuf = *recvbuf;
+		rbuflen = *recvbuflen;
+	}
+
+	sc_format_apdu(card, &apdu,
+			recvbuf ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT,
+			ins, p1, p2);
+
+	apdu.lc = sendbuflen;
+	apdu.datalen = sendbuflen;
+	apdu.data = sendbuf;
+
+	if (recvbuf) {
+		apdu.resp = rbuf;
+		apdu.le = (rbuflen > 255) ? 255 : rbuflen;
+		apdu.resplen = rbuflen;
+	} else {
+		 apdu.resp =  rbuf;
+		 apdu.le = 0;
+		 apdu.resplen = 0;
+	}
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p",
+		apdu.flags, apdu.le, apdu.resplen, apdu.resp);
+
+	/* with new adpu.c and chaining, this actually reads the whole object */
+	r = sc_transmit_apdu(card, &apdu);
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"result r=%d apdu.resplen=%d sw1=%02x sw2=%02x",
+			r, apdu.resplen, apdu.sw1, apdu.sw2);
+	if (r < 0) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed");
+		goto err;
+	}
+
+	if (apdu.sw1 == 0x61) {
+		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+	}
+
+	if (r < 0) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Card returned error ");
+		goto err;
+	}
+
+	if (recvbuflen) {
+		if (recvbuf && *recvbuf == NULL) {
+			*recvbuf =  malloc(apdu.resplen);
+			if (*recvbuf == NULL) {
+				r = SC_ERROR_OUT_OF_MEMORY;
+				goto err;
+			}
+			memcpy(*recvbuf, rbuf, apdu.resplen);
+		}
+		*recvbuflen =  apdu.resplen;
+		r = *recvbuflen;
+	}
+
+err:
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+/*
+ * Read a CAC TLV file. Parameters specify if the TLV file is TL (Tag/Length) file or a V (value) file
+ */
+#define HIGH_BYTE_OF_SHORT(x) (((x)>> 8) & 0xff)
+#define LOW_BYTE_OF_SHORT(x) ((x) & 0xff)
+static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *out_len)
+{
+	u8 params[2];
+	u8 count[2];
+	u8 *out = NULL;
+	u8 *out_ptr;
+	size_t offset = 0;
+	size_t size = 0;
+	size_t left = 0;
+	size_t len;
+	int r;
+
+	params[0] = file_type;
+	params[1] = 2;
+
+	/* get the size */
+	len = sizeof(count);
+	out_ptr = count;
+	r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, &params[0], sizeof(params), &out_ptr, &len);
+	if (r < 0)
+		goto fail;
+
+	left = size = lebytes2ushort(count);
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "got %d bytes out_ptr=%lx count&=%lx count[0]=0x%02x count[1]=0x%02x, len=0x%04x (%d)",
+		len, (unsigned long) out_ptr, (unsigned long)&count, count[0], count[1], size, size);
+	out = out_ptr = malloc(size);
+	if (out == NULL) {
+		r = SC_ERROR_OUT_OF_MEMORY;
+		goto fail;
+	}
+	for (offset += 2; left > 0; offset += len, left -= len, out_ptr += len) {
+		len = MIN(left, CAC_MAX_CHUNK_SIZE);
+		params[1] = len;
+		r = cac_apdu_io(card, CAC_INS_READ_FILE, HIGH_BYTE_OF_SHORT(offset), LOW_BYTE_OF_SHORT(offset),
+						&params[0], sizeof(params), &out_ptr, &len);
+		if (r < 0) {
+			goto fail;
+		}
+	}
+	*out_len = size;
+	*out_buf = out;
+	return SC_SUCCESS;
+fail:
+	if (out)
+		free(out);
+	*out_len = 0;
+	return r;
+}
+
+/*
+ * OLD cac read certificate, only use with CAC-1 card.
+ */
+static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len)
+{
+	u8 buf[CAC_MAX_SIZE];
+	u8 *out_ptr;
+	size_t size = 0;
+	size_t left = 0;
+	size_t len, next_len;
+	sc_apdu_t apdu;
+	int r;
+
+
+	/* get the size */
+	size = left = *out_buf ? *out_len : sizeof(buf);
+	out_ptr = *out_buf ? *out_buf : buf;
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 );
+	next_len = MIN(left, 100);
+	for (; left > 0; left -= len, out_ptr += len) {
+		len = next_len;
+		apdu.resp = out_ptr;
+		apdu.le = len;
+		apdu.resplen = left;
+
+		r = sc_transmit_apdu(card, &apdu);
+		if (r < 0) {
+			break;
+		}
+		/* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */
+		if (apdu.sw1 != 0x63)  {
+			/* we've either finished reading, or hit an error, break */
+			r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+			left -= len;
+			break;
+		}
+		next_len = MIN(left,apdu.sw2);
+	}
+	if (r < 0) {
+		return r;
+	}
+	r = size - left;
+	if (*out_buf == NULL) {
+		*out_buf = malloc(r);
+		if (*out_buf == NULL) {
+			return SC_ERROR_OUT_OF_MEMORY;
+		}
+		memcpy(*out_buf, buf, r);
+	}
+	*out_len = r;
+	return r;
+}
+
+/* Create a fake tag/length file in Simple TLV for cac1 cards based on the val_len.
+ */
+int cac_cac1_get_cert_tag(sc_card_t *card, size_t val_len, u8 **tlp, size_t *tl_len_p)
+{
+	static const u8 cac_cac1_cert_tag[] = { CAC_TAG_CERTINFO, 1, CAC_TAG_CERTIFICATE, 0xff, 0, 0 };
+	u8 *tl, *tl1, *tl2;
+	size_t tl_len;
+	int rv = SC_SUCCESS;
+
+	tl_len = sizeof(cac_cac1_cert_tag);
+	tl = malloc(tl_len);
+	if (tl == NULL)
+		return SC_ERROR_OUT_OF_MEMORY;
+	memcpy(tl, cac_cac1_cert_tag, tl_len);
+
+	rv = sc_simpletlv_put_tag(CAC_TAG_CERTINFO, 1, tl, tl_len, &tl1);
+	if (rv != SC_SUCCESS)
+		goto failure;
+
+	val_len -= 1; /* one byte is CERTINFO Value */
+	tl_len -= (tl1 - tl);
+	rv = sc_simpletlv_put_tag(CAC_TAG_CERTIFICATE, val_len, tl1, tl_len, &tl2);
+	if (rv != SC_SUCCESS)
+		goto failure;
+
+	*tlp = tl;
+	*tl_len_p = (tl2 - tl);
+	return SC_SUCCESS;
+failure:
+	free(tl);
+	return rv;
+}
+
+/*
+ * Callers of this may be expecting a certificate,
+ * select file will have saved the object type for us
+ * as well as set that we want the cert from the object.
+ */
+static int cac_read_binary(sc_card_t *card, unsigned int idx,
+		unsigned char *buf, size_t count, unsigned long flags)
+{
+	cac_private_data_t * priv = CAC_DATA(card);
+	int r = 0;
+	u8 *tl = NULL, *val = NULL;
+	u8 *tl_ptr, *val_ptr, *tlv_ptr, *tl_start;
+	u8 *cert_ptr;
+	size_t tl_len, val_len, tlv_len;
+	size_t len, tl_head_len, cert_len;
+	u8 cert_type, tag;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	/* if we didn't return it all last time, return the remainder */
+	if (priv->cached) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"returning cached value idx=%d count=%d",idx, count);
+		if (idx > priv->cache_buf_len) {
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED);
+		}
+		len = MIN(count, priv->cache_buf_len-idx);
+		memcpy(buf, &priv->cache_buf[idx], len);
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len);
+	}
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"clearing cache idx=%d count=%d",idx, count);
+	if (priv->cache_buf) {
+		free(priv->cache_buf);
+		priv->cache_buf = NULL;
+		priv->cache_buf_len = 0;
+	}
+
+
+	if (priv->object_type <= 0)
+		 SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
+
+	if ((card->type == SC_CARD_TYPE_CAC_I) && (priv->object_type == CAC_OBJECT_TYPE_CERT)) {
+		/* SPICE smart card emulator only presents CAC-1 cards with the old CAC-1 interface as
+		 * certs. If we are a cac 1 card, use the old interface */
+		r = cac_cac1_get_certificate(card, &val, &val_len);
+		if (r < 0)
+			goto done;
+
+		r = cac_cac1_get_cert_tag(card, val_len, &tl, &tl_len);
+		if (r < 0)
+			goto done;
+	} else {
+		r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
+		if (r < 0)  {
+			goto done;
+		}
+
+		r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
+		if (r < 0)
+			goto done;
+	}
+
+	switch (priv->object_type) {
+	case CAC_OBJECT_TYPE_TLV_FILE:
+		tlv_len = tl_len + val_len;
+		priv->cache_buf = malloc(tlv_len);
+		if (priv->cache_buf == NULL) {
+			r = SC_ERROR_OUT_OF_MEMORY;
+			goto done;
+		}
+		priv->cache_buf_len = tlv_len;
+
+		for (tl_ptr = tl, val_ptr=val, tlv_ptr = priv->cache_buf;
+				tl_len > 2 && val_len > 0 && tlv_len > 0;
+				val_len -= len, tlv_len -= len, val_ptr += len, tlv_ptr += len) {
+			/* get the tag and the length */
+			tl_start = tl_ptr;
+			if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
+				break;
+			tl_head_len = (tl_ptr - tl_start);
+			sc_simpletlv_put_tag(tag, len, tlv_ptr, tlv_len, &tlv_ptr);
+			tlv_len -= tl_head_len;
+			tl_len -= tl_head_len;
+
+			/* don't crash on bad data */
+			if (val_len < len) {
+				len = val_len;
+			}
+			/* if we run out of return space, truncate */
+			if (tlv_len < len) {
+				len = tlv_len;
+			}
+			memcpy(tlv_ptr, val_ptr, len);
+		}
+		break;
+
+	case CAC_OBJECT_TYPE_CERT:
+		/* read file */
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL," obj= cert_file, val_len=%d (0x%04x)", val_len, val_len);
+		cert_len = 0;
+		cert_ptr = NULL;
+		cert_type = 0;
+		tl_head_len = 2;
+		for (tl_ptr = tl, val_ptr=val; tl_len >= 2;
+				val_len -= len, val_ptr += len, tl_len -= tl_head_len) {
+			tl_start = tl_ptr;
+			if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
+				break;
+			tl_head_len = tl_ptr - tl_start;
+			if (tag == CAC_TAG_CERTIFICATE) {
+				cert_len = len;
+				cert_ptr = val_ptr;
+			}
+			if (tag == CAC_TAG_CERTINFO) {
+				if ((len >= 1) && (val_len >=1)) {
+					cert_type = *val_ptr;
+				}
+			}
+			if ((val_len < len) || (tl_len < tl_head_len)) {
+				break;
+			}
+		}
+		/* if the info byte is 1, then the cert is compressed, decompress it */
+		if ((cert_type & 0x3) == 1) {
+#ifdef ENABLE_ZLIB
+			r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len,
+				cert_ptr, cert_len, COMPRESSION_AUTO);
+#else
+			sc_log(card->ctx, "PIV compression not supported, no zlib");
+			r = SC_ERROR_NOT_SUPPORTED;
+#endif
+			if (r)
+				goto done;
+		} else {
+			priv->cache_buf = malloc(cert_len);
+			if (priv->cache_buf == NULL) {
+				r = SC_ERROR_OUT_OF_MEMORY;
+				goto done;
+			}
+			priv->cache_buf_len = cert_len;
+			memcpy(priv->cache_buf, cert_ptr, cert_len);
+		}
+		break;
+	default:
+		/* Unknown object type */
+		r = SC_ERROR_INTERNAL;
+		goto done;
+	}
+
+	/* OK we've read the data, now copy the required portion out to the callers buffer */
+	priv->cached = 1;
+	len = MIN(count, priv->cache_buf_len-idx);
+	memcpy(buf, &priv->cache_buf[idx], len);
+	r = len;
+done:
+	if (tl)
+		free(tl);
+	if (val)
+		free(val);
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+/* CAC driver is read only */
+static int cac_write_binary(sc_card_t *card, unsigned int idx,
+		const u8 *buf, size_t count, unsigned long flags)
+{
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
+}
+
+/* initialize getting a list and return the number of elements in the list */
+static int cac_get_init_and_get_count(list_t *list, cac_object_t **entry, int *countp)
+{
+	*countp = list_size(list);
+	list_iterator_start(list);
+	*entry = list_iterator_next(list);
+	return SC_SUCCESS;
+}
+
+/* finalize the list iterator */
+static int cac_final_iterator(list_t *list)
+{
+	list_iterator_stop(list);
+	return SC_SUCCESS;
+}
+
+/* fill in the obj_info for the current object on the list and advance to the next object */
+static int cac_fill_object_info(list_t *list, cac_object_t **entry, sc_pkcs15_data_info_t *obj_info)
+{
+	memset(obj_info, 0, sizeof(sc_pkcs15_data_info_t));
+	if (*entry == NULL) {
+		return SC_ERROR_FILE_END_REACHED;
+	}
+
+	obj_info->path = (*entry)->path;
+	obj_info->path.count = CAC_MAX_SIZE-1; /* read something from the object */
+	obj_info->id.value[0] = ((*entry)->fd >> 8) & 0xff;
+	obj_info->id.value[1] = (*entry)->fd & 0xff;
+	obj_info->id.len = 2;
+	strncpy(obj_info->app_label, (*entry)->name, SC_PKCS15_MAX_LABEL_SIZE-1);
+	*entry = list_iterator_next(list);
+	return SC_SUCCESS;
+}
+
+static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial)
+{
+	cac_private_data_t * priv = CAC_DATA(card);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
+        if (card->serialnr.len)   {
+                *serial = card->serialnr;
+                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+        }
+	if (priv->cac_id_len) {
+		serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR);
+		memcpy(serial->value, priv->cac_id, priv->cac_id_len);
+                SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+	}
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND);
+}
+
+
+static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+{
+	cac_private_data_t * priv = CAC_DATA(card);
+
+	LOG_FUNC_CALLED(card->ctx);
+	sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr);
+
+	if (priv == NULL) {
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL);
+	}
+	switch(cmd) {
+		case SC_CARDCTL_GET_SERIALNR:
+			return cac_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr);
+		case SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS:
+			return cac_get_init_and_get_count(&priv->general_list, &priv->general_current, (int *)ptr);
+		case SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS:
+			return cac_get_init_and_get_count(&priv->pki_list, &priv->pki_current, (int *)ptr);
+		case SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT:
+			return cac_fill_object_info(&priv->general_list, &priv->general_current, (sc_pkcs15_data_info_t *)ptr);
+		case SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT:
+			return cac_fill_object_info(&priv->pki_list, &priv->pki_current, (sc_pkcs15_data_info_t *)ptr);
+		case SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS:
+			return cac_final_iterator(&priv->general_list);
+		case SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS:
+			return cac_final_iterator(&priv->pki_list);
+	}
+
+	LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+}
+
+static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
+{
+	u8 rbuf[8];
+	u8 *rbufp = NULL;
+	size_t rbuflen = 0;
+	int r;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"challenge len=%d",len);
+
+	r = sc_lock(card);
+	if (r != SC_SUCCESS)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+
+	/* CAC requires 8 byte response */
+	while (len > 0) {
+		size_t n;
+
+		rbufp = &rbuf[0];
+		rbuflen = sizeof(rbuf);
+		r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &rbuflen);
+		if (r < 0) {
+			sc_unlock(card);
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+		}
+		n = len > rbuflen ? rbuflen : len;
+		memcpy(rnd, rbufp, n);
+		len -= n;
+		rnd += n;
+	}
+
+	r = sc_unlock(card);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+}
+
+static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
+{
+	int r = SC_SUCCESS;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"flags=%08x op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%d\n",
+			env->flags, env->operation, env->algorithm, env->algorithm_flags,
+			env->algorithm_ref, env->key_ref[0], env->key_ref_len);
+
+	if (env->algorithm != SC_ALGORITHM_RSA) {
+		 r = SC_ERROR_NO_CARD_SUPPORT;
+	}
+
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
+}
+
+
+static int cac_restore_security_env(sc_card_t *card, int se_num)
+{
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+
+static int cac_rsa_op(sc_card_t *card,
+					const u8 * data, size_t datalen,
+					u8 * out, size_t outlen)
+{
+	int r;
+	u8 *outp, *rbuf;
+	size_t rbuflen, outplen;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"datalen=%d outlen=%d\n", datalen, outlen);
+
+	outp = out;
+	outplen = outlen;
+
+	/* Not strictly necessary. This code requires the caller to have selected the correct PKI container
+	 * and authenticated to that container with the verifyPin command... All of this under the reader lock.
+	 * The PKCS #15 higher level driver code does all this correctly (it's the same for all cards, just
+	 * different sets of APDU's that need to be called), so this call is really a little bit of paranoia */
+	r = sc_lock(card);
+	if (r != SC_SUCCESS)
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+
+	rbuf = NULL;
+	rbuflen = 0;
+	for (; datalen > CAC_MAX_CHUNK_SIZE; data += CAC_MAX_CHUNK_SIZE, datalen -= CAC_MAX_CHUNK_SIZE) {
+		r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_STEP,  0,
+			data, CAC_MAX_CHUNK_SIZE, &rbuf, &rbuflen);
+		if (r < 0) {
+			break;
+		}
+		if (rbuflen != 0) {
+			int n = MIN(rbuflen, outplen);
+			memcpy(outp,rbuf, n);
+			outp += n;
+			outplen -= n;
+		}
+		free(rbuf);
+		rbuf = NULL;
+		rbuflen = 0;
+	}
+	if (r < 0) {
+		goto err;
+	}
+	rbuf = NULL;
+	rbuflen = 0;
+	r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_FINAL, 0, data, datalen, &rbuf, &rbuflen);
+	if (r < 0) {
+		goto err;
+	}
+	if (rbuflen != 0) {
+		int n = MIN(rbuflen, outplen);
+		memcpy(outp,rbuf, n);
+		outp += n;
+		outplen -= n;
+	}
+	free(rbuf);
+	rbuf = NULL;
+	r = outlen-outplen;
+
+err:
+	sc_unlock(card);
+	if (r < 0) {
+		sc_mem_clear(out, outlen);
+	}
+	if (rbuf) {
+		free(rbuf);
+	}
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+static int cac_compute_signature(sc_card_t *card,
+					const u8 * data, size_t datalen,
+					u8 * out, size_t outlen)
+{
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
+}
+
+static int cac_decipher(sc_card_t *card,
+					 const u8 * data, size_t datalen,
+					 u8 * out, size_t outlen)
+{
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen));
+}
+
+/*
+ * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more
+ * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead
+ * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS
+ * if it doesn't like anything about the select, so we always 'request' FCI for CAC1
+ *
+ * The rest is just copied from iso7816_select_file
+ */
+static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out, int type)
+{
+	struct sc_context *ctx;
+	struct sc_apdu apdu;
+	unsigned char buf[SC_MAX_APDU_BUFFER_SIZE];
+	unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
+	int r, pathlen, pathtype;
+	struct sc_file *file = NULL;
+	cac_private_data_t * priv = CAC_DATA(card);
+
+	assert(card != NULL && in_path != NULL);
+	ctx = card->ctx;
+
+	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
+
+	memcpy(path, in_path->value, in_path->len);
+	pathlen = in_path->len;
+	pathtype = in_path->type;
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"path->aid=%x %x %x %x %x %x %x  len=%d, path->value = %x %x %x %x len=%d path->type=%d (%x)",
+		in_path->aid.value[0], in_path->aid.value[1], in_path->aid.value[2], in_path->aid.value[3],
+		in_path->aid.value[4], in_path->aid.value[5], in_path->aid.value[6], in_path->aid.len,
+		in_path->value[0], in_path->value[1], in_path->value[2], in_path->value[3], in_path->len,
+		in_path->type, in_path->type);
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"file_out=%lx index=%d count=%d\n",(unsigned long) file_out,
+		in_path->index, in_path->count);
+
+	/* Sigh, sc_key_select expects paths to keys to have specific formats. There is no override.
+	 * we have to add some bytes to the path to make it happy. A better fix would be to give sc_key_file
+	 * a flag that says 'no, really this path is fine'.  We only need to do this for private keys */
+	if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) {
+		if (pathlen > 2) {
+			path += 2;
+			pathlen -= 2;
+		}
+	}
+
+
+	/* CAC has multiple different type of objects that aren't PKCS #15. When we read
+	 * them we need convert them to something PKCS #15 would understand. Find the object
+	 * and object type here:
+	 */
+	if (priv) { /* don't record anything if we haven't been initialized yet */
+		priv->object_type = CAC_OBJECT_TYPE_TLV_FILE;
+		if (cac_is_cert(priv, in_path)) {
+			priv->object_type = CAC_OBJECT_TYPE_CERT;
+		}
+		/* forget any old cacheed values */
+		if (priv->cache_buf) {
+			free(priv->cache_buf);
+			priv->cache_buf = NULL;
+		}
+		priv->cache_buf_len = 0;
+		priv->cached = 0;
+	}
+
+	if (in_path->aid.len) {
+		if (!pathlen) {
+			memcpy(path, in_path->aid.value, in_path->aid.len);
+			pathlen = in_path->aid.len;
+			pathtype = SC_PATH_TYPE_DF_NAME;
+		} else {
+			/* First, select the application */
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" );
+			sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0);
+			apdu.data = in_path->aid.value;
+			apdu.datalen = in_path->aid.len;
+			apdu.lc = in_path->aid.len;
+
+			r = sc_transmit_apdu(card, &apdu);
+			LOG_TEST_RET(ctx, r, "APDU transmit failed");
+			r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+			if (r)
+				LOG_FUNC_RETURN(ctx, r);
+
+		}
+	}
+
+	sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
+
+	switch (pathtype) {
+	/* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select.
+	 * Unfortunately we'd also need to update the caching code as well. For now just
+	 * use FILE_ID and change p1 here */
+	case SC_PATH_TYPE_FILE_ID:
+		apdu.p1 = 2;
+		if (pathlen != 2)
+			return SC_ERROR_INVALID_ARGUMENTS;
+		break;
+	case SC_PATH_TYPE_DF_NAME:
+		apdu.p1 = 4;
+		break;
+	default:
+		LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
+	}
+	apdu.lc = pathlen;
+	apdu.data = path;
+	apdu.datalen = pathlen;
+	apdu.resp = buf;
+	apdu.resplen = sizeof(buf);
+	apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256;
+
+	if (file_out != NULL) {
+		apdu.p2 = 0;		/* first record, return FCI */
+	}
+	else {
+		apdu.p2 = (type == SC_CARD_TYPE_CAC_I)? 0x00 : 0x0C;
+	}
+
+	r = sc_transmit_apdu(card, &apdu);
+	LOG_TEST_RET(ctx, r, "APDU transmit failed");
+	if (file_out == NULL) {
+		/* For some cards 'SELECT' can be only with request to return FCI/FCP. */
+		r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+		if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86)   {
+			apdu.p2 = 0x00;
+			if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS)
+				r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+		}
+		if (apdu.sw1 == 0x61)
+			LOG_FUNC_RETURN(ctx, SC_SUCCESS);
+		LOG_FUNC_RETURN(ctx, r);
+	}
+
+	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+	if (r)
+		LOG_FUNC_RETURN(ctx, r);
+
+		/* CAC cards enver return FCI, fake one */
+	file = sc_file_new();
+	if (file == NULL)
+			LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
+	file->path = *in_path;
+	file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */
+
+	*file_out = file;
+	SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+
+}
+
+static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
+{
+	return cac_select_file_by_type(card, in_path, file_out, card->type);
+}
+
+static int cac_finish(sc_card_t *card)
+{
+	cac_private_data_t * priv = CAC_DATA(card);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	if (priv) {
+		cac_free_private_data(priv);
+	}
+	return SC_SUCCESS;
+}
+
+
+/* select the Card Capabilities Container on CAC-2 */
+static int cac_select_CCC(sc_card_t *card)
+{
+	return cac_select_file_by_type(card, &cac_CCC_Path, NULL, SC_CARD_TYPE_CAC_II);
+}
+
+static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len)
+{
+	if (len < 10) {
+		return SC_ERROR_INVALID_DATA;
+	}
+	sc_mem_clear(path, sizeof(sc_path_t));
+	memcpy(path->aid.value, &val->rid, sizeof(val->rid));
+	memcpy(&path->aid.value[5], &val->applicationID, sizeof(val->applicationID));
+	path->aid.len = sizeof(val->rid) + sizeof(val->applicationID);
+	memcpy(path->value, &val->objectID, sizeof(val->objectID));
+	path->len = sizeof(val->objectID);
+	path->type = SC_PATH_TYPE_FILE_ID;
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"path->aid=%x %x %x %x %x %x %x  len=%d, path->value = %x %x len=%d path->type=%d (%x)",
+		path->aid.value[0], path->aid.value[1], path->aid.value[2], path->aid.value[3],
+		path->aid.value[4], path->aid.value[5], path->aid.value[6],
+		path->aid.len, path->value[0], path->value[1], path->len, path->type, path->type);
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"rid=%x %x %x %x %x  len=%d appid= %x %x len=%d objid= %x %x len=%d",
+		val->rid[0], val->rid[1], val->rid[2], val->rid[3], val->rid[4], sizeof(val->rid),
+		val->applicationID[0], val->applicationID[1], sizeof(val->applicationID),
+		val->objectID[0], val->objectID[1], sizeof(val->objectID));
+
+	return SC_SUCCESS;
+}
+
+static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, int len)
+{
+	cac_object_t new_object;
+	const cac_object_t *obj;
+	unsigned short object_id;
+	int r;
+
+	r = cac_path_from_cardurl(card, &new_object.path, val, len);
+	if (r != SC_SUCCESS) {
+		return r;
+	}
+	switch (val->cardApplicationType) {
+	case CAC_APP_TYPE_PKI:
+		/* we don't want to overflow the cac_label array. This test could
+		 * go way if we create a label function that will create a unique label
+		 * from a cert index.
+		 */
+		if (priv->cert_next >= MAX_CAC_SLOTS)
+			break; /* don't fail just because we have more certs than we can support */
+		new_object.name = cac_labels[priv->cert_next];
+		new_object.fd = priv->cert_next+1;
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name);
+		cac_add_object_to_list(&priv->pki_list, &new_object);
+		priv->cert_next++;
+		break;
+	case CAC_APP_TYPE_GENERAL:
+		object_id = bebytes2ushort(val->objectID);
+		obj = cac_find_obj_by_id(object_id);
+		if (obj == NULL)
+			break; /* don't fail just because we don't recognize the object */
+		new_object.name = obj->name;
+		new_object.fd = 0;
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: gen_object found, objectID=%x (%s),", object_id, new_object.name);
+		cac_add_object_to_list(&priv->general_list, &new_object);
+		break;
+	case CAC_APP_TYPE_SKI:
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found");
+	break;
+	default:
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unkown object_object found (type=0x%x)", val->cardApplicationType);
+		/* don't fail just because there is an unknown object in the CCC */
+		break;
+	}
+	return SC_SUCCESS;
+}
+
+static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t *val, size_t len)
+{
+	size_t card_id_len;
+
+	if (len < sizeof(cac_cuid_t)) {
+		return SC_ERROR_INVALID_DATA;
+	}
+
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "gsc_rid=%s", sc_dump_hex(val->gsc_rid, sizeof(val->gsc_rid)));
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "manufacture id=%x", val->manufacturer_id);
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "cac_type=%d", val->card_type);
+	card_id_len = len - (&val->card_id - (u8 *)val);
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "card_id=%s (%d)",sc_dump_hex(&val->card_id, card_id_len),card_id_len);
+	priv->cuid = *val;
+	priv->cac_id = malloc(card_id_len);
+	if (priv->cac_id == NULL) {
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+	memcpy(priv->cac_id, &val->card_id, card_id_len);
+	priv->cac_id_len = card_id_len;
+	return SC_SUCCESS;
+}
+static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv);
+
+static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl,
+						 size_t tl_len, u8 *val, size_t val_len)
+{
+	size_t len = 0;
+	u8 *tl_end = tl + tl_len;
+	u8 *val_end = val + val_len;
+	sc_path_t new_path;
+	int r;
+
+
+	for (; (tl < tl_end) && (val< val_end); val += len) {
+		/* get the tag and the length */
+		u8 tag;
+		if (sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len) != SC_SUCCESS)
+			break;
+		switch (tag) {
+		case CAC_TAG_CUID:
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CUID");
+			r = cac_parse_cuid(card, priv, (cac_cuid_t *)val, len);
+			if (r < 0)
+				return r;
+			break;
+		case CAC_TAG_CC_VERSION_NUMBER:
+		case CAC_TAG_GRAMMAR_VERION_NUMBER:
+			/* ignore the version numbers for now */
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Version");
+			break;
+		case CAC_TAG_CARDURL:
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL");
+			r = cac_parse_cardurl(card, priv, (cac_card_url_t *)val, len);
+			if (r < 0)
+				return r;
+			break;
+		/*
+		 * The following are really for file systems cards. This code only cares about CAC VM cards
+		 */
+		case CAC_TAG_PKCS15:
+			/* should verify that this is '0'. If it's not zero, we should drop out of here and
+			 * 	let the PKCS 15 code handle this card */
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:PKCS5");
+			break;
+		case CAC_TAG_DATA_MODEL:
+		case CAC_TAG_CARD_APDU:
+		case CAC_TAG_CAPABILITY_TUPLES:
+		case CAC_TAG_STATUS_TUPLES:
+		case CAC_TAG_REDIRECTION:
+		case CAC_TAG_ERROR_CODES:
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:FSSpecific(0x%x)", tag);
+			break;
+		case CAC_TAG_ACCESS_CONTROL:
+			/* handle access control later */
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:ACCESS Control");
+			break;
+		case CAC_TAG_NEXT_CCC:
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:NEXT CCC");
+			r = cac_path_from_cardurl(card, &new_path, (cac_card_url_t *)val, len);
+			if (r < 0)
+				return r;
+
+			r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II);
+			if (r < 0)
+				return r;
+
+			r = cac_process_CCC(card, priv);
+			if (r < 0)
+				return r;
+			break;
+		default:
+			/* ignore tags we don't understand */
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%x)",tag );
+			break;
+		}
+	}
+	return SC_SUCCESS;
+}
+
+static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv)
+{
+	u8 *tl = NULL, *val = NULL;
+	size_t tl_len, val_len;
+	int r;
+
+
+	r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len);
+	if (r < 0)
+		goto done;
+
+	r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len);
+	if (r < 0)
+		goto done;
+
+	r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len);
+done:
+	if (tl)
+		free(tl);
+	if (val)
+		free(val);
+	return r;
+}
+
+/* select a CAC-1 pki applet by index */
+static int cac_select_pki_applet(sc_card_t *card, int index)
+{
+	sc_path_t applet_path = cac_cac1_pki_obj.path;
+	applet_path.aid.value[applet_path.aid.len-1] = index;
+	return cac_select_file_by_type(card, &applet_path, NULL, SC_CARD_TYPE_CAC_I);
+}
+
+/*
+ *  Find the first existing CAC-1 applet. If none found, then this isn't a CAC-1
+ */
+static int cac_find_first_pki_applet(sc_card_t *card, int *index_out)
+{
+	int r, i;
+	for (i=0; i < MAX_CAC_SLOTS; i++) {
+		r = cac_select_pki_applet(card, i);
+		if (r == SC_SUCCESS) {
+			*index_out = i;
+			return r;
+		}
+	}
+	return SC_ERROR_OBJECT_NOT_FOUND;
+}
+
+/*
+ * CAC-1 has been found, identify all the certs and general containers.
+ * This emulates CAC-2's CCC.
+ */
+static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *priv)
+{
+	int r, i;
+	cac_object_t pki_obj = cac_cac1_pki_obj;
+	u8 buf[100];
+	u8 *val;
+	size_t val_len;
+
+	/* populate PKI objects */
+	for (i=index; i < MAX_CAC_SLOTS; i++) {
+		r = cac_select_pki_applet(card, i);
+		if (r == SC_SUCCESS) {
+			pki_obj.name = cac_labels[i];
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: pki_object found, cert_next=%d (%s),", i, pki_obj.name);
+			pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i;
+			pki_obj.fd = i+1; /* don't use id of zero */
+			cac_add_object_to_list(&priv->pki_list, &pki_obj);
+		}
+	}
+
+	/* populate non-PKI objects */
+	for (i=0; i < cac_1_object_count; i++) {
+		r = cac_select_file_by_type(card, &cac_1_objects[i].path, NULL, SC_CARD_TYPE_CAC_I);
+		if (r == SC_SUCCESS) {
+			sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: obj_object found, cert_next=%d (%s),", i, cac_1_objects[i].name);
+			cac_add_object_to_list(&priv->general_list, &cac_1_objects[i]);
+		}
+	}
+
+	/*
+	 * create a cuid to simulate the cac 2 cuid.
+	 */
+	priv->cuid = cac_cac1_cuid;
+	/* create a serial number by hashing the first 100 bytes of the
+	 * first certificate on the card */
+	r = cac_select_pki_applet(card, index);
+	if (r < 0) {
+		return r; /* shouldn't happen unless the card has been removed or is malfunctioning */
+	}
+	val = buf;
+	val_len = sizeof(buf);
+	r = cac_cac1_get_certificate(card, &val, &val_len);
+	if (r >= 0) {
+		priv->cac_id = malloc(20);
+		if (priv->cac_id == NULL) {
+			return SC_ERROR_OUT_OF_MEMORY;
+		}
+#ifdef ENABLE_OPENSSL
+		SHA1(val, val_len, priv->cac_id);
+		priv->cac_id_len = 20;
+#else
+		sc_log(card->ctx, "OpenSSL Required");
+		return SC_ERROR_NOT_SUPPORTED;
+#endif /* ENABLE_OPENSSL */
+	}
+	return SC_SUCCESS;
+}
+
+/*
+ * Look for a CAC card. If it exists, initialize our data structures
+ */
+static int cac_find_and_initialize(sc_card_t *card, int initialize)
+{
+	int r, index;
+	cac_private_data_t *priv = NULL;
+
+	/* already initialized? */
+	if (card->drv_data) {
+		return SC_SUCCESS;
+	}
+
+	/* is this a CAC-2 specified in NIST Interagency Report 6887 -
+	 * "Government Smart Card Interoperability Specification v2.1 July 2003" */
+	r = cac_select_CCC(card);
+	if (r == SC_SUCCESS) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CCC found, is CAC-2");
+		if (!initialize) /* match card only */
+			return r;
+
+		priv = cac_new_private_data();
+		r = cac_process_CCC(card, priv);
+		if (r == SC_SUCCESS) {
+			card->type = SC_CARD_TYPE_CAC_II;
+			card->drv_data = priv;
+			return r;
+		}
+	}
+
+	/* is this a CAC-1 specified in DoD "CAC Applet Developer Guide" version 1.0 September 2002 */
+	r = cac_find_first_pki_applet(card, &index);
+	if (r == SC_SUCCESS) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "applet found, is CAC-1");
+		if (!initialize) /* match card only */
+			return r;
+
+		if (!priv) {
+			priv = cac_new_private_data();
+		}
+		r = cac_populate_cac_1(card, index, priv);
+		if (r == SC_SUCCESS) {
+			card->type = SC_CARD_TYPE_CAC_I;
+			card->drv_data = priv;
+			return r;
+		}
+	}
+	if (priv) {
+		cac_free_private_data(priv);
+	}
+	return r;
+}
+
+
+/* NOTE: returns a bool, 1 card matches, 0 it does not */
+static int cac_match_card(sc_card_t *card)
+{
+	int r;
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	/* Since we send an APDU, the card's logout function may be called...
+	 * however it may be in dirty memory */
+	card->ops->logout = NULL;
+
+	r = cac_find_and_initialize(card, 0);
+	return (r == SC_SUCCESS); /* never match */
+}
+
+
+static int cac_init(sc_card_t *card)
+{
+	int r;
+	unsigned long flags;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	r = cac_find_and_initialize(card, 1);
+	if (r < 0) {
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+	}
+	flags = SC_ALGORITHM_RSA_RAW;
+
+	_sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */
+	_sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
+	_sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
+
+	card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
+{
+	/* CAC, like PIV needs Extra validation of (new) PIN during
+	 * a PIN change request, to ensure it's not outside the
+	 * FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2
+	 * (6 character minimum) requirements.
+	 */
+	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
+
+	if (data->cmd == SC_PIN_CMD_CHANGE) {
+		int i = 0;
+		if (data->pin2.len < 6) {
+			return SC_ERROR_INVALID_PIN_LENGTH;
+		}
+		for(i=0; i < data->pin2.len; ++i) {
+			if (!isdigit(data->pin2.data[i])) {
+				return SC_ERROR_INVALID_DATA;
+			}
+		}
+	}
+
+	return  iso_drv->ops->pin_cmd(card, data, tries_left);
+}
+
+static struct sc_card_operations cac_ops;
+
+static struct sc_card_driver cac_drv = {
+	"Common Access Card (CAC)",
+	"cac",
+	&cac_ops,
+	NULL, 0, NULL
+};
+
+static struct sc_card_driver * sc_get_driver(void)
+{
+	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
+
+	cac_ops = *iso_drv->ops;
+	cac_ops.match_card = cac_match_card;
+	cac_ops.init = cac_init;
+	cac_ops.finish = cac_finish;
+
+	cac_ops.select_file =  cac_select_file; /* need to record object type */
+	cac_ops.get_challenge = cac_get_challenge;
+	cac_ops.read_binary = cac_read_binary;
+	cac_ops.write_binary = cac_write_binary;
+	cac_ops.set_security_env = cac_set_security_env;
+	cac_ops.restore_security_env = cac_restore_security_env;
+	cac_ops.compute_signature = cac_compute_signature;
+	cac_ops.decipher =  cac_decipher;
+	cac_ops.card_ctl = cac_card_ctl;
+	cac_ops.pin_cmd = cac_pin_cmd;
+
+	return &cac_drv;
+}
+
+
+struct sc_card_driver * sc_get_cac_driver(void)
+{
+	return sc_get_driver();
+}
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index 22c654c..d67a83c 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -210,6 +210,17 @@ enum {
 	SC_CARDCTL_PIV_PIN_PREFERENCE,
 	SC_CARDCTL_PIV_OBJECT_PRESENT,
 
+	/*
+	 * CAC specific calls
+	 */
+	SC_CARDCTL_CAC_BASE = _CTL_PREFIX('C', 'A', 'C'),
+	SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS,
+	SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT,
+	SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS,
+	SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS,
+	SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT,
+	SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS,
+
         /*
 	 * AuthentIC v3
 	 */
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index d0ce788..d71c02f 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -228,9 +228,14 @@ enum {
 	/* JPKI cards */
 	SC_CARD_TYPE_JPKI_BASE = 31000,
 
-	/* CAC cards */
 	SC_CARD_TYPE_COOLKEY_BASE = 32000,
 	SC_CARD_TYPE_COOLKEY_GENERIC,
+
+	/* CAC cards */
+	SC_CARD_TYPE_CAC_BASE = 33000,
+	SC_CARD_TYPE_CAC_GENERIC,
+	SC_CARD_TYPE_CAC_I,
+	SC_CARD_TYPE_CAC_II,
 };
 
 extern sc_card_driver_t *sc_get_default_driver(void);
@@ -271,6 +276,7 @@ extern sc_card_driver_t *sc_get_masktech_driver(void);
 extern sc_card_driver_t *sc_get_gids_driver(void);
 extern sc_card_driver_t *sc_get_jpki_driver(void);
 extern sc_card_driver_t *sc_get_coolkey_driver(void);
+extern sc_card_driver_t *sc_get_cac_driver(void);
 
 #ifdef __cplusplus
 }
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index bf0a618..9f296b6 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -111,6 +111,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
 	{ "muscle",	(void *(*)(void)) sc_get_muscle_driver },
 	{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
 	{ "PIV-II",	(void *(*)(void)) sc_get_piv_driver },
+	{ "cac",	(void *(*)(void)) sc_get_cac_driver },
 	{ "itacns",	(void *(*)(void)) sc_get_itacns_driver },
 	{ "isoApplet",	(void *(*)(void)) sc_get_isoApplet_driver },
 #ifdef ENABLE_ZLIB
diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h
index 98a4237..efc076a 100644
--- a/src/libopensc/internal.h
+++ b/src/libopensc/internal.h
@@ -111,6 +111,13 @@ unsigned long bebytes2ulong(const u8 *buf);
  */
 unsigned short bebytes2ushort(const u8 *buf);
 
+/**
+ * Convert 2 bytes in little endian order into an unsigned short
+ * @param  buf   the byte array of 2 bytes
+ * @return       the converted value
+ */
+unsigned short lebytes2ushort(const u8 *buf);
+
 /* Returns an scconf_block entry with matching ATR/ATRmask to the ATR specified,
  * NULL otherwise. Additionally, if card driver is not specified, search through
  * all card drivers user configured ATRs. */
diff --git a/src/libopensc/pkcs15-cac.c b/src/libopensc/pkcs15-cac.c
new file mode 100644
index 0000000..4894fe4
--- /dev/null
+++ b/src/libopensc/pkcs15-cac.c
@@ -0,0 +1,436 @@
+/*
+ * partial PKCS15 emulation for CAC-II cards
+ * only minimal use of the authentication cert and key
+ *
+ * Copyright (C) 2005,2006,2007,2008,2009,2010
+ *               Douglas E. Engert <deengert at anl.gov>
+ *               2004, Nils Larsch <larsch at trustcenter.de>
+ * Copyright (C) 2006, Identity Alliance,
+ *               Thomas Harning <thomas.harning at identityalliance.com>
+ * Copyright (C) 2007, EMC, Russell Larner <rlarner at rsa.com>
+ * Copyright (C) 2016, Red Hat, Inc.
+ *
+ * CAC driver author: Robert Relyea <rrelyea at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "internal.h"
+#include "cardctl.h"
+#include "pkcs15.h"
+/* X509 Key Usage flags */
+#include "../pkcs15init/pkcs15-init.h"
+
+/* probably should get manufacturer ID from cuid */
+#define MANU_ID		"Common Access Card"
+
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *);
+
+
+
+typedef struct pdata_st {
+	const char *id;
+	const char *label;
+	const char *path;
+	int         ref;
+	int         type;
+	unsigned int maxlen;
+	unsigned int minlen;
+	unsigned int storedlen;
+	int         flags;
+	int         tries_left;
+	const unsigned char  pad_char;
+	int         obj_flags;
+} pindata;
+
+static int cac_detect_card(sc_pkcs15_card_t *p15card)
+{
+	sc_card_t *card = p15card->card;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	if (card->type < SC_CARD_TYPE_CAC_GENERIC
+		|| card->type >= SC_CARD_TYPE_CAC_GENERIC+1000)
+		return SC_ERROR_INVALID_CARD;
+	return SC_SUCCESS;
+}
+
+#define CAC_NUM_CERTS_AND_KEYS 10
+
+static const char * cac_get_name(int type)
+{
+    switch (type) {
+    case SC_CARD_TYPE_CAC_I: return ("CAC I");
+    case SC_CARD_TYPE_CAC_II: return ("CAC II");
+    default: break;
+    }
+    return ("CAC");
+}
+
+/*
+ * These could move to a helper file for other cards that wish to use usage as a way of getting flags
+ */
+
+/* Only certain usages are valid for a given algorithm, return all the usages that the algorithm supports so we
+ * can use it as a filter for all the public and private key usages */
+static unsigned int
+cac_alg_flags_from_algorithm(int algorithm)
+{
+	switch (algorithm) {
+	case SC_ALGORITHM_RSA:
+		return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP |
+		       SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER |
+		       SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP |
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
+		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	case SC_ALGORITHM_DSA:
+		return SC_PKCS15_PRKEY_USAGE_VERIFY| SC_PKCS15_PRKEY_USAGE_SIGN |
+		       SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+#ifdef SC_ALGORITHM_DH
+	case SC_ALGORITHM_DH:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE ;
+#endif
+	case SC_ALGORITHM_EC:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	case SC_ALGORITHM_GOSTR3410:
+		return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+		       SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	}
+	return 0;
+}
+
+
+/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */
+#define CAC_X509_USAGE_SIGNATURE		\
+	(SC_PKCS15INIT_X509_DIGITAL_SIGNATURE	| \
+	SC_PKCS15INIT_X509_NON_REPUDIATION	| \
+	SC_PKCS15INIT_X509_KEY_CERT_SIGN 	| \
+	SC_PKCS15INIT_X509_CRL_SIGN)
+#define CAC_X509_USAGE_DERIVE			\
+	SC_PKCS15INIT_X509_KEY_AGREEMENT
+#define CAC_X509_USAGE_UNWRAP 			\
+	(SC_PKCS15INIT_X509_KEY_ENCIPHERMENT	| \
+	SC_PKCS15INIT_X509_KEY_AGREEMENT)
+#define CAC_X509_USAGE_DECRYPT			\
+	(SC_PKCS15INIT_X509_DATA_ENCIPHERMENT 	\
+	/* | encipher? */)
+#define CAC_X509_USAGE_NONREPUDIATION		\
+	SC_PKCS15INIT_X509_NON_REPUDIATION
+
+/* map a cert usage and algorithm to public and private key usages */
+static int
+cac_map_usage(unsigned long long cert_usage, int algorithm, unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, int allow_nonrepudiation)
+{
+	unsigned int pub_usage = 0, pr_usage = 0;
+	unsigned int alg_flags = cac_alg_flags_from_algorithm(algorithm);
+
+	if (cert_usage & CAC_X509_USAGE_SIGNATURE) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;
+	}
+	if (cert_usage & CAC_X509_USAGE_DERIVE) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+	}
+	if (cert_usage & (CAC_X509_USAGE_DECRYPT|CAC_X509_USAGE_UNWRAP)) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT;
+	}
+	if (allow_nonrepudiation && (cert_usage & CAC_X509_USAGE_NONREPUDIATION)) {
+		pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+		pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+	}
+	/* filter usages algorithm */
+	if (pub_usage_ptr) {
+		*pub_usage_ptr = pub_usage & alg_flags;
+	}
+	if (pr_usage_ptr) {
+		*pr_usage_ptr = pr_usage & alg_flags;
+	}
+	return SC_SUCCESS;
+}
+
+static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card)
+{
+	static const pindata pins[] = {
+		{ "1", NULL, "", 0x00,
+		  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
+		  8, 4, 8,
+		  SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
+		  SC_PKCS15_PIN_FLAG_INITIALIZED ,
+		  -1, 0xFF,
+		  SC_PKCS15_CO_FLAG_PRIVATE },
+		{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+	};
+	/* oid for key usage */
+	static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }};
+	unsigned long long usage;
+
+
+	/*
+	 * The size of the key or the algid is not really known
+	 * but can be derived from the certificates.
+	 * the cert, pubkey and privkey are a set.
+	 * Key usages bits taken from certificate key usage extension.
+	 */
+
+	int    r, i;
+	sc_card_t *card = p15card->card;
+	sc_serial_number_t serial;
+	char buf[SC_MAX_SERIALNR * 2 + 1];
+	int count;
+	char *token_name = NULL;
+
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	memset(&serial, 0, sizeof(serial));
+
+	/* could read this off card if needed */
+
+	p15card->tokeninfo->label = strdup(cac_get_name(card->type));
+	p15card->tokeninfo->manufacturer_id = strdup(MANU_ID);
+
+	/*
+	 * get serial number
+	 */
+	r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
+	if (r < 0) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"sc_card_ctl rc=%d",r);
+		p15card->tokeninfo->serial_number = strdup("00000000");
+	} else {
+		sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
+		p15card->tokeninfo->serial_number = strdup(buf);
+	}
+
+	/* set pins */
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding pins...");
+	for (i = 0; pins[i].id; i++) {
+		struct sc_pkcs15_auth_info pin_info;
+		struct sc_pkcs15_object   pin_obj;
+		const char * label;
+
+		memset(&pin_info, 0, sizeof(pin_info));
+		memset(&pin_obj,  0, sizeof(pin_obj));
+
+		pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
+		sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
+		pin_info.attrs.pin.reference     = pins[i].ref;
+		pin_info.attrs.pin.flags         = pins[i].flags;
+		pin_info.attrs.pin.type          = pins[i].type;
+		pin_info.attrs.pin.min_length    = pins[i].minlen;
+		pin_info.attrs.pin.stored_length = pins[i].storedlen;
+		pin_info.attrs.pin.max_length    = pins[i].maxlen;
+		pin_info.attrs.pin.pad_char      = pins[i].pad_char;
+		sc_format_path(pins[i].path, &pin_info.path);
+		pin_info.tries_left    = -1;
+
+		label = pins[i].label? pins[i].label : cac_get_name(card->type);
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC Adding pin %d label=%s",i, label);
+		strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1);
+		pin_obj.flags = pins[i].obj_flags;
+
+		r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
+		if (r < 0)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+	}
+
+	/* set other objects */
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS, &count);
+	for (i = 0; i < count; i++) {
+		struct sc_pkcs15_data_info obj_info;
+		struct sc_pkcs15_object    obj_obj;
+
+		r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT, &obj_info);
+		if (r < 0)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+		memset(&obj_obj, 0, sizeof(obj_obj));
+		memcpy(obj_obj.label, obj_info.app_label, sizeof(obj_obj.label));
+
+		r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT,
+			&obj_obj, &obj_info);
+		if (r < 0)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+	}
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS, &count);
+
+	/*
+	 * certs, pubkeys and priv keys are related and we assume
+	 * they are in order
+	 * We need to read the cert, get modulus and keylen
+	 * We use those for the pubkey, and priv key objects.
+	 */
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding certs, pub and priv keys...");
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS, &count);
+	for (i = 0; i < count; i++) {
+		struct sc_pkcs15_data_info obj_info;
+		struct sc_pkcs15_cert_info cert_info;
+		struct sc_pkcs15_pubkey_info pubkey_info;
+		struct sc_pkcs15_prkey_info prkey_info;
+		struct sc_pkcs15_object cert_obj;
+		struct sc_pkcs15_object pubkey_obj;
+		struct sc_pkcs15_object prkey_obj;
+		sc_pkcs15_der_t   cert_der;
+		sc_pkcs15_cert_t *cert_out;
+
+		r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT, &obj_info);
+
+		memset(&cert_info, 0, sizeof(cert_info));
+		memset(&pubkey_info, 0, sizeof(pubkey_info));
+		memset(&prkey_info, 0, sizeof(prkey_info));
+		memset(&cert_obj,  0, sizeof(cert_obj));
+		memset(&pubkey_obj,  0, sizeof(pubkey_obj));
+		memset(&prkey_obj,  0, sizeof(prkey_obj));
+
+		cert_info.id = obj_info.id;
+		pubkey_info.id = obj_info.id;
+		prkey_info.id = obj_info.id;
+		cert_info.path = obj_info.path;
+		prkey_info.path = obj_info.path;
+		/* Add 0x3f00 to the front of prkey_info.path to make sc_key_file happy */
+		/* only do this if our path.len is 1 or 2 */
+		if (prkey_info.path.len && prkey_info.path.len <= 2) {
+			prkey_info.path.value[2] = prkey_info.path.value[0];
+			prkey_info.path.value[3] = prkey_info.path.value[1];
+			prkey_info.path.value[0] = 0x3f;
+			prkey_info.path.value[1] = 0x00;
+			prkey_info.path.len += 2;
+		}
+		pubkey_info.native        = 1;
+		pubkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1];
+		prkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1];
+		prkey_info.native        = 1;
+
+		memcpy(cert_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+		memcpy(pubkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+		memcpy(prkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+		prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+		sc_pkcs15_format_id(pins[0].id, &prkey_obj.auth_id);
+
+		r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len);
+
+		if (r) {
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No cert found,i=%d", i);
+			continue;
+		}
+		cert_info.path.count = cert_der.len;
+
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cert len=%d, cert_info.path.count=%d r=%d\n",
+					cert_der.len, cert_info.path.count, r);
+		sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "cert", cert_der.value, cert_der.len);
+
+		/* cache it using the PKCS15 emulation objects */
+		/* as it does not change */
+		if (cert_der.value) {
+			cert_info.value.value = cert_der.value;
+			cert_info.value.len = cert_der.len;
+			cert_info.path.len = 0; /* use in mem cert from now on */
+		}
+
+		/* following will find the cached cert in cert_info */
+		r =  sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
+		if (r < 0 || cert_out->key == NULL) {
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r);
+			continue;
+		}
+
+		r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
+		if (r < 0) {
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " Failed to add cert obj r=%d",r);
+			continue;
+		}
+		/* set the token name to the name of the CN of the first certificate */
+		if (!token_name) {
+			u8 * cn_name = NULL;
+			size_t cn_len = 0;
+			static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }};
+			r = sc_pkcs15_get_name_from_dn(card->ctx, cert_out->subject,
+				cert_out->subject_len, &cn_oid, &cn_name, &cn_len);
+			if (r == SC_SUCCESS) {
+				token_name = malloc (cn_len+1);
+				memcpy(token_name, cn_name, cn_len);
+				free(cn_name);
+				token_name[cn_len] = 0;
+				free(p15card->tokeninfo->label);
+				p15card->tokeninfo->label = token_name;
+			}
+		}
+
+
+		r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, cert_out->key, &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len);
+		if (r < 0)
+			goto fail;
+		pubkey_obj.emulated = cert_out->key;
+
+		r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL);
+		if (r < 0) {
+			usage = 0xd9ULL; /* basic default usage */
+		}
+		cac_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1);
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,  "cert %s: cert_usage=0x%llx, pub_usage=0x%x priv_usage=0x%x\n",
+				sc_dump_hex(cert_info.id.value, cert_info.id.len),
+				 usage, pubkey_info.usage, prkey_info.usage);
+		if (cert_out->key->algorithm != SC_ALGORITHM_RSA) {
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"unsupported key.algorithm %d", cert_out->key->algorithm);
+			continue;
+		} else {
+			pubkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+			prkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+			r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa public key r=%d usage=%x",r, pubkey_info.usage);
+			if (r < 0)
+				goto fail;
+			r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
+			sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa private key r=%d usage=%x",r, prkey_info.usage);
+		}
+
+		cert_out->key = NULL;
+fail:
+		sc_pkcs15_free_certificate(cert_out);
+		if (r < 0)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */
+
+	}
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS, &count);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card,
+		struct sc_aid *aid, sc_pkcs15emu_opt_t *opts)
+{
+	sc_card_t   *card = p15card->card;
+	sc_context_t    *ctx = card->ctx;
+
+	LOG_FUNC_CALLED(ctx);
+
+	if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
+		return sc_pkcs15emu_cac_init(p15card);
+	else {
+		int r = cac_detect_card(p15card);
+		if (r)
+			return SC_ERROR_WRONG_CARD;
+		return sc_pkcs15emu_cac_init(p15card);
+	}
+}
diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c
index bcc0e5a..440f531 100644
--- a/src/libopensc/pkcs15-cert.c
+++ b/src/libopensc/pkcs15-cert.c
@@ -264,7 +264,7 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert,
 
 		/*
 		 * use the sc_asn1_decoder for clarity. NOTE it would be more efficient to do this by hand
-		 * so we avoid the man malloc/frees here, but one hopes that one day the asn1_decode will allow
+		 * so we avoid the many malloc/frees here, but one hopes that one day the asn1_decode will allow
 		 * a 'static pointer' flag that returns a const pointer to the actual asn1 space so we only need
 		 * to make a final copy of the extension value before we return */
 		critical = 0;
@@ -316,7 +316,7 @@ sc_pkcs15_get_bitstring_extension(struct sc_context *ctx,
 	u8 *bit_string = NULL;
 	size_t bit_string_len=0, val_len = sizeof(*value);
 	struct sc_asn1_entry asn1_bit_string[] = {
-		{ "bitString", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len },
+		{ "bitString", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len },
 		{ NULL, 0, 0, 0, NULL, NULL }
 	};
 
@@ -325,6 +325,7 @@ sc_pkcs15_get_bitstring_extension(struct sc_context *ctx,
 
 	r = sc_asn1_decode(ctx, asn1_bit_string, bit_string, bit_string_len, NULL, NULL);
 	LOG_TEST_RET(ctx, r, "Decoding extension bit string");
+	free(bit_string);
 
 	LOG_FUNC_RETURN(ctx, SC_SUCCESS);
 }
diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
index 0163f48..0fdcbed 100644
--- a/src/libopensc/pkcs15-syn.c
+++ b/src/libopensc/pkcs15-syn.c
@@ -44,6 +44,7 @@ struct sc_pkcs15_emulator_handler builtin_emulators[] = {
 	{ "itacns",	sc_pkcs15emu_itacns_init_ex	},
 	{ "postecert",	sc_pkcs15emu_postecert_init_ex  },
 	{ "PIV-II",     sc_pkcs15emu_piv_init_ex	},
+	{ "cac",        sc_pkcs15emu_cac_init_ex	},
 	{ "gemsafeGPK",	sc_pkcs15emu_gemsafeGPK_init_ex	},
 	{ "gemsafeV1",	sc_pkcs15emu_gemsafeV1_init_ex	},
 	{ "actalis",	sc_pkcs15emu_actalis_init_ex	},
diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h
index 5e3ae59..22db061 100644
--- a/src/libopensc/pkcs15-syn.h
+++ b/src/libopensc/pkcs15-syn.h
@@ -37,6 +37,7 @@ int sc_pkcs15emu_tcos_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_
 int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_opt_t *);
 int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_opt_t *);
 int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
 int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
 int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
 int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c
index 626c522..306ec57 100644
--- a/src/libopensc/sc.c
+++ b/src/libopensc/sc.c
@@ -178,6 +178,13 @@ unsigned short bebytes2ushort(const u8 *buf)
 	return (unsigned short) (buf[0] << 8 | buf[1]);
 }
 
+unsigned short lebytes2ushort(const u8 *buf)
+{
+	if (buf == NULL)
+		return 0U;
+	return (unsigned short)buf[1] << 8 | (unsigned short)buf[0];
+}
+
 void sc_init_oid(struct sc_object_id *oid)
 {
 	int ii;
diff --git a/src/libopensc/simpletlv.c b/src/libopensc/simpletlv.c
new file mode 100644
index 0000000..f526a1c
--- /dev/null
+++ b/src/libopensc/simpletlv.c
@@ -0,0 +1,99 @@
+/*
+ * simpletlv.c: Simple TLV encoding and decoding functions
+ *
+ * Copyright (C) 2016  Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ *          Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "internal.h"
+#include "simpletlv.h"
+
+/*
+ * Put a tag/length record to a file in Simple TLV based on the  datalen
+ * content length.
+ */
+int
+sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr)
+{
+	u8 *p = out;
+
+	if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
+		return SC_ERROR_INVALID_ARGUMENTS;
+
+	/* tag is just number between 0x01 and 0xFE */
+	if (tag == 0x00 || tag == 0xff)
+		return SC_ERROR_INVALID_ARGUMENTS;
+
+	*p++ = tag; /* tag is single byte */
+	if (datalen < 0xff) {
+		/* short value up to 255 */
+		*p++ = (u8)datalen; /* is in the second byte */
+	} else if (datalen < 0xffff) {
+		/* longer values up to 65535 */
+		*p++ = (u8)0xff; /* first byte is 0xff */
+		*p++ = (u8)datalen & 0xff;
+		*p++ = (u8)(datalen >> 8) & 0xff; /* LE */
+	} else {
+		/* we can't store more than two bytes in Simple TLV */
+		return SC_ERROR_WRONG_LENGTH;
+	}
+	if (ptr != NULL)
+		*ptr = p;
+	return SC_SUCCESS;
+}
+
+/* Read the TL file and return appropriate tag and the length of associated
+ * content.
+ */
+int
+sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen)
+{
+	size_t len;
+	u8 *p = *buf;
+
+	if (buflen < 2) {
+		*buf = p+buflen;
+		return SC_ERROR_INVALID_ARGUMENTS;
+	}
+
+	*tag_out = *p++;
+	len = *p++;
+	if (len == 0xff) {
+		/* don't crash on bad data */
+		if (buflen < 4) {
+			*taglen = 0;
+			return SC_ERROR_INVALID_ARGUMENTS;
+		}
+		len = lebytes2ushort(p);
+		p++;
+	}
+	*taglen = len;
+	*buf = p;
+	return SC_SUCCESS;
+}
diff --git a/src/libopensc/simpletlv.h b/src/libopensc/simpletlv.h
new file mode 100644
index 0000000..c207441
--- /dev/null
+++ b/src/libopensc/simpletlv.h
@@ -0,0 +1,54 @@
+/*
+ * simpletlv.h: Simple TLV header file
+ *
+ * Copyright (C) 2016  Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ *          Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef _OPENSC_SIMPLETLV_H
+#define _OPENSC_SIMPLETLV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libopensc/opensc.h"
+#include "libopensc/pkcs15.h"
+
+/*
+ * Create a tag/length file in Simple TLV based on the  val_len  content length
+ * @param  tag       Tag to store into the TL file
+ * @param  datalen   Data length to store into the TL file
+ * @param  out       TL byte array to write into
+ * @param  outlen    The length of the output array
+ * @param  ptr       The end of the TL record written
+ * @return           SC_SUCCESS for correct input
+ */
+int sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr);
+
+/* get the Simple TLV tag and length.
+ * @param  buf       Pointer to the TL file
+ * @param  buflen    The length of TL file
+ * @param  tag_out   The tag from the TL file
+ * @param  taglen    The length of the V record
+ * @return           SC_SUCCESS on valid input
+ */
+int sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen);
+
+#endif

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-opensc/opensc.git



More information about the pkg-opensc-commit mailing list