[pkg-opensc-commit] [opensc] 111/295: Add Coolkey driver

Eric Dorland eric at moszumanska.debian.org
Sat Jun 24 21:11:21 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 790ed5fcd7a3189c2df686daebe02f43ae5a72c3
Author: Jakub Jelen <jjelen at redhat.com>
Date:   Fri Oct 14 10:45:56 2016 +0200

    Add Coolkey driver
    
    Author: Robert Relyea <rrelyea at redhat.com>
    
    Coolkey driver improvements:
     * Remove hardcoded list and use SimCList
     * Whitespace cleanup
     * Remove bogus if
     * drop inline keywords
     * proper path to include sys/types.h
     * full name of ushort type
     * condition to use compression
     * proper include path
     * Resolve template name conflict in Tokend
    
    Clean up the copyright headers
    
    -- rebased into one commit by VTA
    -- closes #896
---
 src/libopensc/Makefile.am      |    4 +-
 src/libopensc/Makefile.mak     |    4 +-
 src/libopensc/card-coolkey.c   | 2365 ++++++++++++++++++++++++++++++++++++++++
 src/libopensc/cardctl.h        |   45 +
 src/libopensc/cards.h          |    5 +
 src/libopensc/ctx.c            |    1 +
 src/libopensc/pkcs15-coolkey.c |  695 ++++++++++++
 src/libopensc/pkcs15-syn.c     |    1 +
 src/libopensc/pkcs15-syn.h     |    1 +
 9 files changed, 3117 insertions(+), 4 deletions(-)

diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index c589e95..39056ce 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -35,7 +35,7 @@ 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-entersafe.c card-epass2003.c card-coolkey.c \
 	card-incrypto34.c card-piv.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-ias.c \
@@ -48,7 +48,7 @@ libopensc_la_SOURCES = \
 	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-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 0b8124b..8c92000 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -17,13 +17,13 @@ OBJECTS			= \
 	card-cardos.obj card-tcos.obj card-default.obj \
 	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-entersafe.obj card-epass2003.obj card-coolkey.obj \
 	card-incrypto34.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-ias.obj \
 	card-itacns.obj card-authentic.obj \
 	card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \
-	card-sc-hsm.obj card-dnie.obj card-isoApplet.obj \
+	card-sc-hsm.obj card-dnie.obj card-isoApplet.obj pkcs15-coolkey.obj \
 	card-masktech.obj card-gids.obj card-jpki.obj \
 	\
 	pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
diff --git a/src/libopensc/card-coolkey.c b/src/libopensc/card-coolkey.c
new file mode 100644
index 0000000..8a70102
--- /dev/null
+++ b/src/libopensc/card-coolkey.c
@@ -0,0 +1,2365 @@
+/*
+ * card-coolkey.c: Support for Coolkey
+ *
+ * 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.
+ *
+ * Coolkey 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
+
+#include <sys/types.h>
+
+#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 "asn1.h"
+#include "cardctl.h"
+#ifdef ENABLE_ZLIB
+#include "compression.h"
+#endif
+#include "iso7816.h"
+#include "../pkcs11/pkcs11.h"
+
+
+
+#define COOLKEY_MAX_SIZE 4096		/* arbitrary, just needs to be 'large enough' */
+
+/*
+ *  COOLKEY hardware and APDU constants
+ */
+#define COOLKEY_MAX_CHUNK_SIZE 240 /* must be less than 255-8 */
+
+/* ISO 7816 CLA values used by COOLKEY */
+#define ISO7816_CLASS           0x00
+#define GLOBAL_PLATFORM_CLASS   0x80
+#define COOLKEY_CLASS           0xb0
+
+/* ISO 71816 INS values used by COOLKEY */
+#define ISO7816_INS_SELECT_FILE 0xa4
+#define ISO7816_INS_GET_DATA    0xca
+
+/* COOLKEY specific INS values (public) */
+#define COOLKEY_INS_GET_LIFE_CYCLE             0xf2
+#define COOLKEY_INS_GET_STATUS                 0x3c
+#define COOLKEY_INS_VERIFY_PIN                 0x42
+#define COOLKEY_INS_LIST_OBJECTS               0x58
+
+/* COOLKEY specific INS values (require nonce) */
+#define COOLKEY_INS_COMPUTE_CRYPT              0x36
+#define COOLKEY_INS_COMPUTE_ECC_KEY_AGREEMENT  0x37
+#define COOLKEY_INS_COMPUTE_ECC_SIGNATURE      0x38
+#define COOLKEY_INS_GET_RANDOM                 0x73
+#define COOLKEY_INS_READ_OBJECT                0x56
+#define COOLKEY_INS_WRITE_OBJECT               0x54
+#define COOLKEY_INS_LOGOUT                     0x61
+
+/* COMPUTE_CRYPT and COMPUT_ECC parameters */
+#define COOLKEY_CRYPT_INIT     1
+#define COOLKEY_CRYPT_PROCESS  2
+#define COOLKEY_CRYPT_FINAL    3
+#define COOLKEY_CRYPT_ONE_STEP 4
+
+#define COOLKEY_CRYPT_MODE_RSA_NO_PAD    0x00
+#define COOLKEY_CRYPT_LOCATION_APDU      0x01
+#define COOLKEY_CRYPT_LOCATION_DL_OBJECT 0x02
+#define COOLKEY_CRYPT_DIRECTION_ENCRYPT  0x03
+
+/* List Objects parameters */
+#define COOLKEY_LIST_RESET 0x00
+#define COOLKEY_LIST_NEXT  0x01
+
+/* Special object identifiers */
+#define COOLKEY_DL_OBJECT_ID       0xffffffff
+#define COOLKEY_COMBINED_OBJECT_ID 0x7a300000 /* 'z0\0\0' */
+#define COOLKEY_INVALID_KEY        0xff00
+#define COOLKEY_KEY_CLASS			'k'
+#define COOLKEY_NONCE_SIZE  8
+
+/* returned from the coolkey extended life cycle apdu */
+typedef struct coolkey_life_cycle {
+	u8 life_cycle;
+	u8 pin_count;
+	u8 protocol_version_major;
+	u8 protocol_version_minor;
+} coolkey_life_cycle_t;
+
+/* return by the coolkey status apdu */
+typedef struct coolkey_status {
+	u8 protocol_version_major;
+	u8 protocol_version_minor;
+	u8 applet_major_version;
+	u8 applet_minor_version;
+	u8 total_object_memory[4];
+	u8 free_object_memory[4];
+	u8 pin_count;
+	u8 key_count;
+	u8 logged_in_identities[2];
+} coolkey_status_t;
+
+/* returned by the iso get status apdu with the global platform cplc data parameters */
+typedef struct global_platform_cplc_data {
+	u8 tag[2];
+	u8 length;
+	u8 ic_fabricator[2];
+	u8 ic_type[2];
+	u8 os_id[2];
+	u8 os_date[2];
+	u8 os_level[2];
+	u8 fabrication_data[2];
+	u8 ic_serial_number[4];
+	u8 ic_batch[2];
+	u8 module_fabricator[2];
+	u8 packing_data[2];
+	u8 icc_manufacturer[2];
+	u8 ic_embedding_data[2];
+	u8 pre_personalizer[2];
+	u8 ic_pre_personalization_data[2];
+	u8 ic_pre_personalization_id[4];
+	u8 ic_personalizaer[2];
+	u8 ic_personalization_data[2];
+	u8 ic_personalization_id[4];
+} global_platform_cplc_data_t;
+
+/* format of the coolkey_cuid, either contructed from cplc data or read from the combined object */
+typedef struct coolkey_cuid {
+	u8 ic_fabricator[2];
+	u8 ic_type[2];
+	u8 ic_batch[2];
+	u8 ic_serial_number[4];
+} coolkey_cuid_t;
+
+/* parameter for list objects apdu */
+typedef struct coolkey_object_info {
+	u8 object_id[4];
+	u8 object_length[4];
+	u8 read_acl[2];
+	u8 write_acl[2];
+	u8 delete_acl[2];
+} coolkey_object_info_t;
+
+/* parameter for the read object apdu */
+typedef struct coolkey_read_object_param {
+	u8 object_id[4];
+	u8 offset[4];
+	u8 length;
+} coolkey_read_object_param_t;
+
+/* parameter for the write object apdu */
+typedef struct coolkey_write_object_param {
+	coolkey_read_object_param_t head;
+	u8 buf[COOLKEY_MAX_CHUNK_SIZE];
+} coolkey_write_object_param_t;
+
+/* coolkey uses muscle like objects, but when coolkey is managed by the TPS system
+ * it creates a single object and encodes the individual objects inside the
+ * common single object. This allows more efficient reading of all the objects
+ * (because we can use a single apdu call and we can compress all the objects
+ * together and take advantage of the fact that many of the certs share the same subject and issue). */
+typedef struct coolkey_combined_header {
+	u8	format_version[2];
+	u8	object_version[2];
+	coolkey_cuid_t cuid;
+	u8	compression_type[2];
+	u8	compression_length[2];
+	u8	compression_offset[2];
+} coolkey_combined_header_t;
+
+#define COOLKEY_COMPRESSION_NONE 0
+#define COOLKEY_COMPRESSION_ZLIB 1
+
+/*
+ * This is the header of the decompressed portion of the combined object
+ */
+typedef struct coolkey_decompressed_header {
+	u8 object_offset[2];
+	u8 object_count[2];
+	u8 token_name_length;
+	u8 token_name[255];      /* arbitary size up to token_name_length */
+} coolkey_decompressed_header_t;
+
+/*
+ * header for an object. There are 2 types of object headers, v1 and v0.
+ * v1 is the most common, and is always found in a combined object, so
+ * we only specify the v0 in the name of the structure.
+ */
+
+typedef struct coolkey_v0_object_header {
+	u8 record_type;				 /* version 0 or version 1 */
+	u8 object_id[4]; 			 /*  coolkey object id  */
+	u8 attribute_data_len[2];    /* the length in bytes of the next block of
+								  * attribute records */
+	/* followed by the first attribute record */
+} coolkey_v0_object_header_t;
+
+typedef struct coolkey_v0_attribute_header {
+	u8 attribute_attr_type[4];	/* CKA_ATTRIBUTE_TYPE */
+	u8 attribute_data_len[2];	/* Length of the attribute */
+	/* followed by the actual attribute data */
+} coolkey_v0_attribute_header_t;
+
+/* combined objects are v1 objects without the record_type indicator */
+typedef struct coolkey_combined_object_header {
+	u8 object_id[4]; 			 /*  coolkey object id  */
+	u8 fixed_attributes_values[4]; /* compressed fixed attributes */
+	u8 attribute_count[2];		/* the number of attribute records that follow */
+	/* followed by the first attribute */
+} coolkey_combined_object_header_t;
+
+typedef struct coolkey_object_header {
+	u8 record_type;				/* version 0 or version 1 */
+	u8 object_id[4]; 			 /*  coolkey object id  */
+	u8 fixed_attributes_values[4]; /* compressed fixed attributes */
+	u8 attribute_count[2];		/* the number of attribute records that follow */
+	/* followed by the first attribute */
+} coolkey_object_header_t;
+
+#define COOLKEY_V0_OBJECT 0
+#define COOLKEY_V1_OBJECT 1
+
+/* vi attribute header */
+typedef struct coolkey_attribute_header {
+	u8 attribute_attr_type[4]; /* CKA_ATTRIBUTE_TYPE */
+	u8 attribute_data_type;    /* the Type of data stored */
+	/* optional attribute data, or attribute len+data, depending on the value of data_type */
+} coolkey_attribute_header_t;
+
+/* values for attribute_data_type */
+#define COOLKEY_ATTR_TYPE_STRING      0
+#define COOLKEY_ATTR_TYPE_INTEGER     1
+#define COOLKEY_ATTR_TYPE_BOOL_FALSE  2
+#define COOLKEY_ATTR_TYPE_BOOL_TRUE   3
+
+/*
+ * format of the fix_attribute values. These are stored as a big endian uint32_t with the below bit field
+ * Definitions:
+ *
+struct coolkey_fixed_attributes_values {
+	uint32_t  cka_id:4;
+	uint32_t  cka_class:3;
+	uint32_t  cka_token:1;
+	uint32_t  cka_private:1;
+	uint32_t  cka_modifiable:1;
+	uint32_t  cka_derive:1;
+	uint32_t  cka_local:1;
+	uint32_t  cka_encrypt:1;
+	uint32_t  cka_decrypt:1;
+	uint32_t  cka_wrap:1;
+	uint32_t  cka_unwrap:1;
+	uint32_t  cka_sign:1;
+	uint32_t  cka_sign_recover:1;
+	uint32_t  cka_verify:1;
+	uint32_t  cka_verify_recover:1;
+	uint32_t  cka_sensitive:1;
+	uint32_t  cka_always_sensitive:1;
+	uint32_t  cka_extractable:1;
+	uint32_t  cka_never_extractable:1;
+	uint32_t  reseved:8;
+};
+
+ *  cka_class is used to determine which booleans are valid. Any attributes in the full attribute list
+ *  takes precidence over the fixed attributes. That is if there is a CKA_ID in the full attribute list,
+ *  The cka_id in the fixed_attributes is ignored. When determining which boolean attribute is valid, the
+ *  cka_class in the fixed attributes are used, even if it is overridden by the  full attribute list.
+ * valid cka_class values and their corresponding valid bools are as follows:
+ *
+ *     0 CKO_DATA                          cka_private, cka_modifiable, cka_token
+ *     1 CKO_CERTIFICATE                   cka_private, cka_modifiable, cka_token
+ *     2 CKO_PUBLIC_KEY                    cka_private, cka_modifiable, cka_token
+ *                                         cka_derive, cka_local, cka_encrypt, cka_wrap
+ *                                         cka_verify, cka_verify_recover
+ *     3 CKO_PRIVATE_KEY                   cka_private, cka_modifiable, cka_token
+ *                                         cka_derive, cka_local, cka_decrypt, cka_unwrap
+ *                                         cka_sign, cka_sign_recover, cka_sensitive,
+ *                                         cka_always_sensitive, cka_extractable,
+ *                                         cka_never_extractable
+ *     4 CKO_SECRET_KEY                    cka_private, cka_modifiable, cka_token
+ *                                         cka_derive, cka_local, cka_encrypt, cka_decrypt,
+ *                                         cka_wrap, cka_unwrap, cka_sign, cka_verify,
+ *                                         cka_sensitive, cka_always_sensitive,
+ *                                         cka_extractable, cka_never_extractable
+ *     5-7 RESERVED                        none
+ *
+ */
+
+/*
+ * Coolkey attribute record handling functions.
+ */
+
+/* get the length of the attribute from a V1 attribute header. If encoded_len == true, then return the length of
+ * the attribute data field (including any explicit length values, If encoded_len = false return the length of
+ * the actual attribute data.
+ */
+static int
+coolkey_v1_get_attribute_len(const u8 *attr, size_t buf_len, size_t *len, int encoded_len)
+{
+	coolkey_attribute_header_t *attribute_head = (coolkey_attribute_header_t *)attr;
+
+	*len = 0;
+	/* don't reference beyond our buffer */
+	if (buf_len < sizeof(coolkey_attribute_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	switch (attribute_head->attribute_data_type) {
+	case COOLKEY_ATTR_TYPE_STRING:
+		if (buf_len < (sizeof(coolkey_attribute_header_t) +2)) {
+			break;
+		}
+		*len = bebytes2ushort(attr + sizeof(coolkey_attribute_header_t));
+		if (encoded_len) {
+			*len += 2;
+		}
+		return SC_SUCCESS;
+	case COOLKEY_ATTR_TYPE_BOOL_FALSE:
+	case COOLKEY_ATTR_TYPE_BOOL_TRUE:
+		/* NOTE: there is no encoded data from TYPE_BOOL_XXX, so we return length 0, but the length
+		 * of the attribute is actually 1 byte, so if encoded_len == false, return 1 */
+		*len = encoded_len ? 0: 1;
+		return SC_SUCCESS;
+		break;
+	case COOLKEY_ATTR_TYPE_INTEGER:
+		*len = 4; /* length is 4 in both encoded length and attribute length */
+		return SC_SUCCESS;
+	default:
+		break;
+	}
+	return SC_ERROR_CORRUPTED_DATA;
+}
+
+/* length of the attribute data is stored in the header of the v0 record */
+static int
+coolkey_v0_get_attribute_len(const u8 *attr, size_t buf_len, size_t *len)
+{
+	coolkey_v0_attribute_header_t *attribute_head = (coolkey_v0_attribute_header_t *)attr;
+	/* don't reference beyond our buffer */
+	if (buf_len < sizeof(coolkey_v0_attribute_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	*len = bebytes2ushort(attribute_head->attribute_data_len);
+	return SC_SUCCESS;
+}
+
+/* these next 3 functions gets the length of the full attribute record, including
+ * the attribute header */
+static size_t
+coolkey_v1_get_attribute_record_len(const u8 *attr, size_t buf_len)
+{
+	size_t attribute_len = sizeof(coolkey_attribute_header_t);
+	size_t len = 0;
+	int r;
+
+	r = coolkey_v1_get_attribute_len(attr, buf_len, &len, 1);
+	if (r < 0) {
+		return buf_len; /* skip to the end, ignore the rest of the record */
+	}
+
+	return MIN(buf_len,attribute_len+len);
+}
+
+
+static size_t
+coolkey_v0_get_attribute_record_len(const u8 *attr, size_t buf_len)
+{
+	size_t attribute_len = sizeof(coolkey_v0_attribute_header_t);
+	size_t len;
+	int r;
+
+	r = coolkey_v0_get_attribute_len(attr, buf_len, &len);
+	if (r < 0) {
+		return buf_len; /* skip to the end, ignore the rest of the record */
+	}
+	return MIN(buf_len,attribute_len+len);
+}
+
+static size_t
+coolkey_get_attribute_record_len(const u8 *attr, u8 obj_record_type, size_t buf_len)
+{
+	if (obj_record_type ==  COOLKEY_V0_OBJECT) {
+		return coolkey_v0_get_attribute_record_len(attr, buf_len);
+	}
+	if (obj_record_type != COOLKEY_V1_OBJECT) {
+		return buf_len; /* skip to the end */
+	}
+	return coolkey_v1_get_attribute_record_len(attr, buf_len);
+}
+
+/*
+ * Attribute type shows up in the same place in all attribute record types. Carry record_type in case
+ * this changes in the future.
+ */
+static CK_ATTRIBUTE_TYPE
+coolkey_get_attribute_type(const u8 *attr, u8 obj_record_type, size_t buf_len)
+{
+	coolkey_attribute_header_t *attribute_header = (coolkey_attribute_header_t *) attr;
+
+	return bebytes2ulong(attribute_header->attribute_attr_type);
+}
+
+/*
+ * return the start of the attribute section based on the record type
+ */
+static const u8 *
+coolkey_attribute_start(const u8 *obj, u8 object_record_type, size_t buf_len)
+{
+	size_t offset = object_record_type == COOLKEY_V1_OBJECT ? sizeof(coolkey_object_header_t) :
+			sizeof(coolkey_v0_object_header_t);
+
+	if ((object_record_type != COOLKEY_V1_OBJECT) && (object_record_type != COOLKEY_V0_OBJECT)) {
+		return NULL;
+	}
+	if (offset > buf_len) {
+		return NULL;
+	}
+	return obj + offset;
+}
+
+/*
+ * We don't have the count in the header for v0 attributes,
+ * Count them.
+ */
+static int
+coolkey_v0_get_attribute_count(const u8 *obj, size_t buf_len)
+{
+	coolkey_v0_object_header_t *object_head = (coolkey_v0_object_header_t *)obj;
+	const u8 *attr;
+	int count = 0;
+	size_t attribute_data_len;
+
+	/* make sure we have enough of the object to read the record_type */
+	if (buf_len <= sizeof(coolkey_v0_object_header_t)) {
+		return 0;
+	}
+	/*
+	 * now loop through all the attributes in the list. first find the start of the list
+	 */
+	attr = coolkey_attribute_start(obj, COOLKEY_V0_OBJECT, buf_len);
+	if (attr == NULL) {
+		return 0;
+	}
+
+	buf_len -= (attr-obj);
+	attribute_data_len = bebytes2ushort(object_head->attribute_data_len);
+	if (buf_len < attribute_data_len) {
+		return 0;
+	}
+
+	while (attribute_data_len) {
+		size_t len = coolkey_v0_get_attribute_record_len(attr, buf_len);
+
+		if (len == 0) {
+			break;
+		}
+		/*  This is an error in the token data, don't parse the last attribute */
+		if (len > attribute_data_len) {
+			break;
+		}
+		/* we know that coolkey_v0_get_attribute_record_len never
+		 * 	returns more than buf_len, so we can safely assert that.
+		 * 	If the assert is true, you can easily see that the loop
+		 * 	will eventually break with len == 0, even if attribute_data_len
+		 * 	was invalid */
+		assert(len <= buf_len);
+		count++;
+		attr += len;
+		buf_len -= len;
+		attribute_data_len -= len;
+	}
+	return count;
+}
+
+static int
+coolkey_v1_get_attribute_count(const u8 *obj, size_t buf_len)
+{
+	coolkey_object_header_t *object_head = (coolkey_object_header_t *)obj;
+
+	if (buf_len <= sizeof(coolkey_object_header_t)) {
+		return 0;
+	}
+	return bebytes2ushort(object_head->attribute_count);
+}
+
+static int
+coolkey_get_attribute_count(const u8 *obj, u8 object_record_type, size_t buf_len)
+{
+	if (object_record_type == COOLKEY_V0_OBJECT) {
+		return coolkey_v0_get_attribute_count(obj, buf_len);
+	}
+	if (object_record_type != COOLKEY_V1_OBJECT) {
+		return 0;
+	}
+	return coolkey_v1_get_attribute_count(obj, buf_len);
+}
+
+/*
+ * The next three functions return a parsed attribute value from an attribute record.
+ */
+static int
+coolkey_v0_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out)
+{
+	/* we need to manually detect types CK_ULONG */
+	CK_ATTRIBUTE_TYPE attr_type = coolkey_get_attribute_type(attr, COOLKEY_V0_OBJECT, buf_len);
+	int r;
+	size_t len;
+
+	attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING;
+	attr_out->attribute_length = 0;
+	attr_out->attribute_value = NULL;
+
+	r = coolkey_v0_get_attribute_len(attr, buf_len, &len);
+	if (r < 0) {
+		return r;
+	}
+	if ((attr_type == CKA_CLASS) || (attr_type == CKA_CERTIFICATE_TYPE)
+									 || (attr_type == CKA_KEY_TYPE)) {
+		if (len != 4) {
+			return SC_ERROR_CORRUPTED_DATA;
+		}
+		attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG;
+	}
+	/* return the length and the data */
+	attr_out->attribute_length = len;
+	attr_out->attribute_value = attr+sizeof(coolkey_v0_attribute_header_t);
+	return SC_SUCCESS;
+}
+
+static u8 coolkey_static_false = CK_FALSE;
+static u8 coolkey_static_true = CK_TRUE;
+
+static int
+coolkey_v1_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out)
+{
+	int r;
+	size_t len;
+	coolkey_attribute_header_t *attribute_head = (coolkey_attribute_header_t *)attr;
+
+	if (buf_len < sizeof(coolkey_attribute_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+
+	/* we must have type V1. Process according to data type */
+	switch (attribute_head->attribute_data_type) {
+	/* ULONG has implied length of 4 */
+	case COOLKEY_ATTR_TYPE_INTEGER:
+		if (buf_len < (sizeof(coolkey_attribute_header_t) + 4)) {
+			return SC_ERROR_CORRUPTED_DATA;
+		}
+		attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG;
+		attr_out->attribute_length = 4;
+		attr_out->attribute_value = attr + sizeof(coolkey_attribute_header_t);
+		return SC_SUCCESS;
+	/* BOOL_FALSE and BOOL_TRUE have implied length and data */
+	/* return type STRING for BOOLS */
+	case COOLKEY_ATTR_TYPE_BOOL_FALSE:
+		attr_out->attribute_length = 1;
+		attr_out->attribute_value =  &coolkey_static_false;
+		return SC_SUCCESS;
+	case COOLKEY_ATTR_TYPE_BOOL_TRUE:
+		attr_out->attribute_length = 1;
+		attr_out->attribute_value =  &coolkey_static_true;
+		return SC_SUCCESS;
+	/* string type has encoded length */
+	case COOLKEY_ATTR_TYPE_STRING:
+		r = coolkey_v1_get_attribute_len(attr, buf_len, &len, 0);
+		if (r < SC_SUCCESS) {
+			return r;
+		}
+		if (buf_len < (len + sizeof(coolkey_attribute_header_t) + 2)) {
+			return SC_ERROR_CORRUPTED_DATA;
+		}
+		attr_out->attribute_value = attr+sizeof(coolkey_attribute_header_t)+2;
+		attr_out->attribute_length = len;
+		return SC_SUCCESS;
+	default:
+		break;
+	}
+	return SC_ERROR_CORRUPTED_DATA;
+}
+
+int
+coolkey_get_attribute_data(const u8 *attr, u8 object_record_type, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out)
+{
+	/* handle the V0 objects first */
+	if (object_record_type == COOLKEY_V0_OBJECT) {
+		return coolkey_v0_get_attribute_data(attr, buf_len, attr_out);
+	}
+
+	/* don't crash if we encounter some new or corrupted coolkey device */
+	if (object_record_type != COOLKEY_V1_OBJECT) {
+		return SC_ERROR_NO_CARD_SUPPORT;
+	}
+
+	return coolkey_v1_get_attribute_data(attr, buf_len, attr_out);
+
+}
+
+/* convert an attribute type into a  bit in the fixed attribute uint32_t  */
+static unsigned long
+coolkey_get_fixed_boolean_bit(CK_ATTRIBUTE_TYPE type)
+{
+	switch(type) {
+	case CKA_TOKEN:               return 0x00000080;
+	case CKA_PRIVATE:             return 0x00000100;
+	case CKA_MODIFIABLE:          return 0x00000200;
+	case CKA_DERIVE:              return 0x00000400;
+	case CKA_LOCAL:               return 0x00000800;
+	case CKA_ENCRYPT:             return 0x00001000;
+	case CKA_DECRYPT:             return 0x00002000;
+	case CKA_WRAP:                return 0x00004000;
+	case CKA_UNWRAP:              return 0x00008000;
+	case CKA_SIGN:                return 0x00010000;
+	case CKA_SIGN_RECOVER:        return 0x00020000;
+	case CKA_VERIFY:              return 0x00040000;
+	case CKA_VERIFY_RECOVER:      return 0x00080000;
+	case CKA_SENSITIVE:           return 0x00100000;
+	case CKA_ALWAYS_SENSITIVE:    return 0x00200000;
+	case CKA_EXTRACTABLE:         return 0x00400000;
+	case CKA_NEVER_EXTRACTABLE:   return 0x00800000;
+	default: break;
+	}
+	return 0; /* return no bits */
+}
+/* This table lets us return a pointer to the CKA_ID value without allocating data or
+ * creating a changable static that could cause thread issues */
+static const u8 coolkey_static_cka_id[16] = {
+0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+};
+
+/* This table provides the following:
+ *     1) a mapping from a 3 bit cka_class to a full 32 bit CKA_CLASS_TYPE value we can return.
+ *     2) the mask of valid boolean attributes in the fixed attributes.
+ */
+struct coolkey_fixed_class {
+	u8 class_value[4];
+	unsigned long boolean_mask;
+};
+
+static const struct coolkey_fixed_class coolkey_static_cka_class[8] = {
+	{ { 0, 0, 0, 0}, 0x00000380 }, /* DATA */
+	{ { 0, 0, 0, 1}, 0x00000380 }, /* CERTIFICATE */
+	{ { 0, 0, 0, 2}, 0x000c5f80 }, /* PUBLIC_KEY */
+	{ { 0, 0, 0, 3}, 0x00f3af80 }, /* PRIVATE_KEY */
+	{ { 0, 0, 0, 4}, 0x00f5ff80 }, /* SECRET_KEY */
+	{ { 0, 0, 0, 5}, 0x00000000 },
+	{ { 0, 0, 0, 6}, 0x00000000 },
+	{ { 0, 0, 0, 7}, 0x00000000 }
+};
+
+/*
+ * handle fixed attributes (V1 only)
+ */
+static int
+coolkey_get_attribute_data_fixed(CK_ATTRIBUTE_TYPE attr_type, unsigned long fixed_attributes,
+																sc_cardctl_coolkey_attribute_t *attr_out) {
+	unsigned long cka_id = fixed_attributes & 0xf;
+	unsigned long cka_class = ((fixed_attributes) >> 4) & 0x7;
+	unsigned long mask, bit;
+
+	if (attr_type == CKA_ID) {
+		attr_out->attribute_length = 1;
+		attr_out->attribute_value= &coolkey_static_cka_id[cka_id];
+		return SC_SUCCESS;
+	}
+	if (attr_type == CKA_CLASS) {
+		attr_out->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG;
+		attr_out->attribute_length = 4;
+		attr_out->attribute_value = coolkey_static_cka_class[cka_class].class_value;
+		return SC_SUCCESS;
+	}
+	/* If it matched, it must be one of the booleans */
+	mask = coolkey_static_cka_class[cka_class].boolean_mask;
+	bit = coolkey_get_fixed_boolean_bit(attr_type);
+	/* attribute isn't in the list */
+	if ((bit & mask) == 0) {
+		return SC_ERROR_DATA_OBJECT_NOT_FOUND;
+	}
+	attr_out->attribute_length = 1;
+	attr_out->attribute_value = bit & fixed_attributes ? &coolkey_static_true : &coolkey_static_false;
+	return SC_SUCCESS;
+}
+
+
+
+static int
+coolkey_v1_get_object_length(u8 *obj, size_t buf_len)
+{
+	coolkey_combined_object_header_t *object_head = (coolkey_combined_object_header_t *) obj;
+	int attribute_count;
+	u8 *current_attribute;
+	int j;
+	size_t len;
+
+	len = sizeof(coolkey_combined_object_header_t);
+	if (buf_len <= len) {
+		return buf_len;
+	}
+	attribute_count = bebytes2ushort(object_head->attribute_count);
+	buf_len -= len;
+
+	for (current_attribute = obj + len, j = 0; j < attribute_count; j++) {
+		size_t attribute_len = coolkey_v1_get_attribute_record_len(current_attribute, buf_len);
+
+		len += attribute_len;
+		current_attribute += attribute_len;
+		buf_len -= attribute_len;
+	}
+	return len;
+}
+
+/*
+ * COOLKEY private data per card state
+ */
+typedef struct coolkey_private_data {
+	u8 protocol_version_major;
+	u8 protocol_version_minor;
+	u8 format_version_major;
+	u8 format_version_minor;
+	unsigned short object_version;
+	u8 life_cycle;
+	u8 pin_count;
+	u8 *token_name;				/* our token name read from the token */
+	size_t token_name_length;		/* length of our token name */
+	u8 nonce[COOLKEY_NONCE_SIZE];		/* nonce returned from login */
+	int nonce_valid;
+	coolkey_cuid_t cuid;			/* card unique ID from the CCC */
+	sc_cardctl_coolkey_object_t *obj;	/* pointer to the current selected object */
+	list_t objects_list;			/* list of objects on the token */
+	unsigned short key_id;			/* key id set by select */
+	int	algorithm;			/* saved from set_security_env */
+	int operation;				/* saved from set_security_env */
+} coolkey_private_data_t;
+
+#define COOLKEY_DATA(card) ((coolkey_private_data_t*)card->drv_data)
+
+int
+coolkey_compare_id(const void * a, const void *b)
+{
+	if (a == NULL || b == NULL)
+		return 1;
+	return ((sc_cardctl_coolkey_object_t *)a)->id
+	    == ((sc_cardctl_coolkey_object_t *)b)->id;
+}
+
+static coolkey_private_data_t *coolkey_new_private_data(void)
+{
+	coolkey_private_data_t *priv;
+	/* allocate priv and zero all the fields */
+	priv = calloc(1, sizeof(coolkey_private_data_t));
+	/* set other fields as appropriate */
+	priv->key_id = COOLKEY_INVALID_KEY;
+	list_init(&priv->objects_list);
+	list_attributes_comparator(&priv->objects_list, coolkey_compare_id);
+
+	return priv;
+}
+
+static void coolkey_free_private_data(coolkey_private_data_t *priv)
+{
+	list_destroy(&priv->objects_list);
+	if (priv->token_name) {
+		free(priv->token_name);
+	}
+	free(priv);
+	return;
+}
+
+/*
+ * Object list operations
+ */
+static int coolkey_add_object_to_list(list_t *list, const sc_cardctl_coolkey_object_t *object)
+{
+	sc_cardctl_coolkey_object_t *entry = malloc(sizeof(sc_cardctl_coolkey_object_t));
+
+	if (entry == NULL) {
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+	*entry = *object;
+	list_append(list, entry);
+	return SC_SUCCESS;
+}
+
+#define COOLKEY_AID "\xA0\x00\x00\x01\x16"
+static sc_cardctl_coolkey_object_t *
+coolkey_find_object_by_id(list_t *list, unsigned long object_id)
+{
+	int pos;
+	static sc_cardctl_coolkey_object_t cmp = {{
+		"", 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+		{ COOLKEY_AID, sizeof(COOLKEY_AID)-1 }
+	}, 0, 0, NULL};
+
+	cmp.id = object_id;
+	if ((pos = list_locate(list, &cmp)) < 0)
+		return NULL;
+
+	return list_get_at(list, pos);
+}
+
+
+static const sc_path_t coolkey_template_path = {
+	"", 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+	{ COOLKEY_AID, sizeof(COOLKEY_AID)-1 }
+};
+
+struct coolkey_error_codes_st {
+	int sc_error;
+	char *description;
+};
+
+static const struct coolkey_error_codes_st coolkey_error_codes[]= {
+	{SC_ERROR_UNKNOWN,                       "Reservered 0x9c00" },
+	{SC_ERROR_NOT_ENOUGH_MEMORY,             "No memory left on card" },
+	{SC_ERROR_PIN_CODE_INCORRECT,            "Authentication failed" },
+	{SC_ERROR_NOT_ALLOWED,                   "Operation not allowed" },
+	{SC_ERROR_UNKNOWN,                       "Reservered 0x9c04" },
+	{SC_ERROR_NO_CARD_SUPPORT,               "Unsupported feature" },
+	{SC_ERROR_SECURITY_STATUS_NOT_SATISFIED, "Not authorized" },
+	{SC_ERROR_DATA_OBJECT_NOT_FOUND,         "Object not found" },
+	{SC_ERROR_FILE_ALREADY_EXISTS,           "Object exists" },
+	{SC_ERROR_NO_CARD_SUPPORT,               "Incorrect Algorithm" },
+	{SC_ERROR_UNKNOWN,                       "Reservered 0x9c0a" },
+	{SC_ERROR_SM_INVALID_CHECKSUM,           "Signature invalid" },
+	{SC_ERROR_AUTH_METHOD_BLOCKED,           "Identity blocked" },
+	{SC_ERROR_UNKNOWN,                       "Reservered 0x9c0d" },
+	{SC_ERROR_UNKNOWN,                       "Reservered 0x9c0e" },
+	{SC_ERROR_INCORRECT_PARAMETERS,          "Invalid parameter" },
+	{SC_ERROR_INCORRECT_PARAMETERS,          "Incorrect P1" },
+	{SC_ERROR_INCORRECT_PARAMETERS,          "Incorrect P2" },
+	{SC_ERROR_FILE_END_REACHED,              "Sequence End" },
+};
+
+static const unsigned int
+coolkey_number_of_error_codes = sizeof(coolkey_error_codes)/sizeof(coolkey_error_codes[0]);
+
+static int coolkey_check_sw(sc_card_t *card, unsigned int sw1, unsigned int sw2)
+{
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
+		"sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2);
+
+	if (sw1 == 0x90)
+		return SC_SUCCESS;
+
+	if (sw1 == 0x9c) {
+		if (sw2 == 0xff) {
+			/* shouldn't happen on a production applet, 0x9cff is a debugging error code */
+			return SC_ERROR_INTERNAL;
+		}
+		if (sw2 >= coolkey_number_of_error_codes) {
+			return SC_ERROR_UNKNOWN;
+		}
+		return coolkey_error_codes[sw2].sc_error;
+	}
+
+	/* iso error */
+        return sc_get_iso7816_driver()->ops->check_sw(card, sw1, sw2);
+}
+
+/*
+ * 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. The coolkey version
+ * adds the coolkey nonce to user authenticated operations.
+ */
+
+static int coolkey_apdu_io(sc_card_t *card, int cla, int ins, int p1, int p2,
+	const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, size_t * recvbuflen,
+	const u8 *nonce, size_t nonce_len)
+{
+	int r;
+	sc_apdu_t apdu;
+	u8 rbufinitbuf[COOLKEY_MAX_SIZE];
+	u8 rsendbuf[COOLKEY_MAX_SIZE];
+	u8 *rbuf;
+	size_t rbuflen;
+	int cse = 0;
+
+
+	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;
+	}
+
+	if (sendbuf || nonce) {
+		if (recvbuf) {
+			cse = SC_APDU_CASE_4_SHORT;
+		} else {
+			cse = SC_APDU_CASE_3_SHORT;
+		}
+	} else {
+		if (recvbuf) {
+			cse = SC_APDU_CASE_2_SHORT;
+		} else {
+			cse = SC_APDU_CASE_1;
+		}
+	}
+
+	/* append the nonce if we have it. Coolkey just blindly puts this at the end
+	 * of the APDU (while adjusting lc). This converts case 1 to case 3. coolkey
+	 * also always drops le in case 4 (which happens when proto = T0). nonces are
+	 * never used on case 2 commands, so we can simply append the nonce to the data
+	 * and we should be fine */
+	if (nonce) {
+		u8 *buf = rsendbuf;
+		if (sendbuf) {
+			sendbuflen = MIN(sendbuflen,sizeof(rsendbuf)-nonce_len);
+			memcpy(rsendbuf, sendbuf, sendbuflen);
+			buf += sendbuflen;
+		}
+		memcpy(buf, nonce, nonce_len);
+		sendbuflen += nonce_len;
+		sendbuf =rsendbuf;
+	}
+
+	sc_format_apdu(card, &apdu, cse, ins, p1, p2);
+
+	apdu.lc = sendbuflen;
+	apdu.datalen = sendbuflen;
+	apdu.data = sendbuf;
+
+
+	/* coolkey uses non-standard classes */
+	apdu.cla = cla;
+
+	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;
+	}
+	r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+	if (r < 0) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed");
+		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);
+}
+
+/*
+ * Helpers to handle coolkey commands
+ */
+static int
+coolkey_get_life_cycle(sc_card_t *card, coolkey_life_cycle_t *life_cycle)
+{
+	coolkey_status_t status;
+	u8 *receive_buf;
+	size_t len;
+	int r;
+
+	len = sizeof(*life_cycle);
+	receive_buf = (u8 *)life_cycle;
+	r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0,
+			NULL, 0, &receive_buf, &len, NULL, 0);
+	if (r == sizeof(*life_cycle)) {
+		return SC_SUCCESS;
+	}
+
+	len = 1;
+	receive_buf = &life_cycle->life_cycle;
+	r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0,
+			NULL, 0, &receive_buf, &len, NULL, 0);
+	if (r < 0) {
+		return r;
+	}
+	len = sizeof(status);
+	receive_buf = (u8 *)&status;
+	r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_STATUS, 0, 0,
+			NULL, 0, &receive_buf, &len, NULL, 0);
+	if (r < 0) {
+		return r;
+	}
+	life_cycle->protocol_version_major = status.protocol_version_major;
+	life_cycle->protocol_version_minor = status.protocol_version_minor;
+	life_cycle->pin_count = status.pin_count;
+	return SC_SUCCESS;
+}
+
+
+/* should be general global platform call */
+static int
+coolkey_get_cplc_data(sc_card_t *card, global_platform_cplc_data_t *cplc_data)
+{
+	size_t len = sizeof(global_platform_cplc_data_t);
+	u8 *receive_buf = (u8 *)cplc_data;
+	return coolkey_apdu_io(card, GLOBAL_PLATFORM_CLASS, ISO7816_INS_GET_DATA, 0x9f, 0x7f,
+			NULL, 0, &receive_buf, &len,  NULL, 0);
+}
+
+/* select the coolkey applet */
+static int coolkey_select_applet(sc_card_t *card)
+{
+	u8 aid[] = { 0x62, 0x76, 0x01, 0xff, 0x00, 0x00, 0x00 };
+	return coolkey_apdu_io(card, ISO7816_CLASS, ISO7816_INS_SELECT_FILE, 4, 0,
+			&aid[0], sizeof(aid), NULL, NULL,  NULL, 0);
+}
+
+static void
+coolkey_make_cuid_from_cplc(coolkey_cuid_t *cuid, global_platform_cplc_data_t *cplc_data)
+{
+	cuid->ic_fabricator[0]    = cplc_data->ic_fabricator[0];
+	cuid->ic_fabricator[1]    = cplc_data->ic_fabricator[1];
+	cuid->ic_type[0]          = cplc_data->ic_type[0];
+	cuid->ic_type[1]          = cplc_data->ic_type[1];
+	cuid->ic_batch[0]         = cplc_data->ic_batch[0];
+	cuid->ic_batch[1]         = cplc_data->ic_batch[1];
+	cuid->ic_serial_number[0] = cplc_data->ic_serial_number[0];
+	cuid->ic_serial_number[1] = cplc_data->ic_serial_number[1];
+	cuid->ic_serial_number[2] = cplc_data->ic_serial_number[2];
+	cuid->ic_serial_number[3] = cplc_data->ic_serial_number[3];
+}
+
+/*
+ * Read a COOLKEY coolkey object.
+ */
+static int coolkey_read_object(sc_card_t *card, unsigned long object_id, size_t offset,
+			u8 *out_buf, size_t out_len, u8 *nonce, size_t nonce_size)
+{
+	coolkey_read_object_param_t params;
+	u8 *out_ptr;
+	size_t left = 0;
+	size_t len;
+	int r;
+
+	ulong2bebytes(&params.object_id[0], object_id);
+
+	out_ptr = out_buf;
+	left = out_len;
+	do {
+		ulong2bebytes(&params.offset[0], offset);
+		params.length = MIN(left, COOLKEY_MAX_CHUNK_SIZE);
+		len = left+2;
+		r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_READ_OBJECT, 0, 0,
+			(u8 *)&params, sizeof(params), &out_ptr, &len, nonce, nonce_size);
+		if (r < 0) {
+			goto fail;
+		}
+		/* santity check to make sure we don't overflow left */
+		if ((left < len) || (len == 0)) {
+			r = SC_ERROR_INTERNAL;
+			goto fail;
+		}
+		out_ptr += len;
+		offset += len;
+		left -= len;
+	} while (left != 0);
+
+	return out_len;
+
+fail:
+	return r;
+}
+
+/*
+ * Write a COOLKEY coolkey object.
+ */
+static int coolkey_write_object(sc_card_t *card, unsigned long object_id,
+			size_t offset, const u8 *buf, size_t buf_len, const u8 *nonce, size_t nonce_size)
+{
+	coolkey_write_object_param_t params;
+	size_t operation_len;
+	size_t left = buf_len;
+	int r;
+
+	ulong2bebytes(&params.head.object_id[0], object_id);
+
+	do {
+		ulong2bebytes(&params.head.offset[0], offset);
+		operation_len = MIN(left, COOLKEY_MAX_CHUNK_SIZE);
+		params.head.length = operation_len;
+		memcpy(params.buf, buf, operation_len);
+		r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_WRITE_OBJECT, 0, 0,
+			(u8 *)&params, sizeof(params.head)+operation_len, NULL, 0, nonce, nonce_size);
+		if (r < 0) {
+			goto fail;
+		}
+		buf += operation_len;
+		offset += operation_len;
+		left -= operation_len;
+	} while (left != 0);
+
+	return buf_len - left;
+
+fail:
+	return r;
+}
+
+/*
+ * coolkey_read_binary will read a coolkey object off the card. That object is selected
+ * by select file. If we've already read the object, we'll return the data from the cache.
+ * coolkey objects are encoded PKCS #11 entries, not pkcs #15 data. pkcs15-coolkey will
+ * translate the objects into their PKCS #15 equivalent data structures.
+ */
+static int coolkey_read_binary(sc_card_t *card, unsigned int idx,
+		u8 *buf, size_t count, unsigned long flags)
+{
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	int r = 0, len;
+	u8 *data = NULL;;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	if (idx > priv->obj->length) {
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED);
+	}
+
+	/* if we've already read the data, just return it */
+	if (priv->obj->data) {
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"returning cached value idx=%d count=%d",idx, count);
+		len = MIN(count, priv->obj->length-idx);
+		memcpy(buf, &priv->obj->data[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);
+
+	data = malloc(priv->obj->length);
+	if (data == NULL) {
+		r = SC_ERROR_OUT_OF_MEMORY;
+		goto done;
+	}
+
+
+	r = coolkey_read_object(card, priv->obj->id, 0, data, priv->obj->length,
+												priv->nonce, sizeof(priv->nonce));
+	if (r < 0)
+		goto done;
+
+	if ((size_t) r != priv->obj->length) {
+		priv->obj->length = r;
+	}
+
+
+	/* OK we've read the data, now copy the required portion out to the callers buffer */
+	len = MIN(count, priv->obj->length-idx);
+	memcpy(buf, &data[idx], len);
+	r = len;
+	/* cache the data in the object */
+	priv->obj->data=data;
+	data = NULL;
+
+done:
+	if (data)
+		free(data);
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+/* COOLKEY driver is read only. NOTE: The applet supports w/r operations, so it's perfectly
+ * reasonable to try to create new objects, but currently TPS does not create applets
+ * That allow user created objects, so this is a nice 2.0 feature. */
+static int coolkey_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 coolkey_get_init_and_get_count(list_t *list, int *countp)
+{
+	*countp = list_size(list);
+	list_iterator_start(list);
+	return SC_SUCCESS;
+}
+
+/* fill in the obj_info for the current object on the list and advance to the next object */
+static int coolkey_fetch_object(list_t *list, sc_cardctl_coolkey_object_t *coolkey_obj)
+{
+	sc_cardctl_coolkey_object_t *ptr;
+	if (!list_iterator_hasnext(list)) {
+		return SC_ERROR_FILE_END_REACHED;
+	}
+
+	ptr = list_iterator_next(list);
+	*coolkey_obj = *ptr;
+	return SC_SUCCESS;
+}
+
+/* Finalize iterator */
+static int coolkey_final_iterator(list_t *list)
+{
+	list_iterator_stop(list);
+	return SC_SUCCESS;
+}
+
+static char * coolkey_cuid_to_string(coolkey_cuid_t *cuid)
+{
+	char *buf;
+	size_t len = sizeof(coolkey_cuid_t)*2 + 1;
+	buf = malloc(len);
+	if (buf == NULL) {
+		return NULL;
+	}
+	sc_bin_to_hex((u8 *)cuid, sizeof(*cuid), buf, len, 0);
+	return buf;
+}
+
+static const struct manufacturer_list_st {
+	unsigned short id;
+	char *string;
+} manufacturer_list[] = {
+	{ 0x2050, "%04x Oberthur" },
+	{ 0x4090, "%04x GemAlto (Infineon)" },
+	{ 0x4780, "%04x STMicroElectronics" },
+	{ 0x4780, "%04x RSA" },
+	{ 0x534e, "%04x SafeNet" },
+};
+
+int manufacturer_list_count = sizeof(manufacturer_list)/sizeof(manufacturer_list[0]);
+
+static char * coolkey_get_manufacturer(coolkey_cuid_t *cuid)
+{
+	unsigned short fabricator = bebytes2ushort(cuid->ic_fabricator);
+	int i;
+	char *buf;
+	const char *manufacturer_string = "%04x Unknown";
+	size_t len;
+	int r;
+
+	for (i=0; i < manufacturer_list_count; i++) {
+		if (manufacturer_list[i].id == fabricator) {
+			manufacturer_string = manufacturer_list[i].string;
+			break;
+		}
+	}
+	len = strlen(manufacturer_string)+1;
+	buf= malloc(len);
+	if (buf == NULL) {
+		return NULL;
+	}
+	r = snprintf(buf, len, manufacturer_string, fabricator);
+	if (r < 0) {
+		free(buf);
+		return NULL;
+	}
+	return buf;
+}
+
+
+static int coolkey_get_token_info(sc_card_t *card, sc_pkcs15_tokeninfo_t * token_info)
+{
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	char *label = NULL;
+	char *manufacturer_id = NULL;
+	char *serial_number = NULL;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
+	label = strdup((char *)priv->token_name);
+	manufacturer_id = coolkey_get_manufacturer(&priv->cuid);
+	serial_number = coolkey_cuid_to_string(&priv->cuid);
+
+	if (label && manufacturer_id && serial_number) {
+		token_info->label = label;
+		token_info->manufacturer_id = manufacturer_id;
+		token_info->serial_number = serial_number;
+		return SC_SUCCESS;
+	}
+	free(label);
+	free(manufacturer_id);
+	free(serial_number);
+	return SC_ERROR_OUT_OF_MEMORY;
+}
+
+static int coolkey_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial)
+{
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
+	memcpy(serial->value, &priv->cuid, sizeof(priv->cuid));
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+int
+coolkey_fill_object(sc_card_t *card, sc_cardctl_coolkey_object_t *obj)
+{
+	int r;
+	size_t buf_len = obj->length;
+	u8 *new_obj_data = malloc(buf_len);
+	sc_cardctl_coolkey_object_t *obj_entry;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+
+	if (obj->data != NULL) {
+		return SC_SUCCESS;
+	}
+	new_obj_data = malloc(buf_len);
+	if (new_obj_data == NULL) {
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+	r = coolkey_read_object(card, obj->id, 0, new_obj_data, buf_len,
+				priv->nonce, sizeof(priv->nonce));
+	if (r != (int)buf_len) {
+		free(new_obj_data);
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	obj_entry = coolkey_find_object_by_id(&priv->objects_list, obj->id);
+	if (obj_entry == NULL) {
+		free(new_obj_data);
+		return SC_ERROR_INTERNAL; /* shouldn't happen */
+	}
+	if (obj_entry->data != NULL) {
+		free(new_obj_data);
+		return SC_ERROR_INTERNAL; /* shouldn't happen */
+	}
+	obj_entry->data = new_obj_data;
+	obj->data = new_obj_data;
+	return SC_SUCCESS;
+}
+
+/*
+ * return a parsed record for the attribute which includes value, type, and length.
+ * Handled both v1 and v0 record types. determine record type from the object.
+ *  make sure we don't overrun the buffer if the token gives us bad data.
+ */
+static int
+coolkey_find_attribute(sc_card_t *card, sc_cardctl_coolkey_attribute_t *attribute)
+{
+	u8 object_record_type;
+	CK_ATTRIBUTE_TYPE attr_type = attribute->attribute_type;
+	const u8 *obj = attribute->object->data;
+	const u8 *attr = NULL;
+	size_t buf_len = attribute->object->length;
+	coolkey_object_header_t *object_head = (coolkey_object_header_t *)obj;
+	int attribute_count,i;
+	attribute->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING;
+	attribute->attribute_length = 0;
+	attribute->attribute_value = NULL;
+
+	if (obj == NULL) {
+		/* cast away const so we can cache the data value */
+		int r = coolkey_fill_object(card, (sc_cardctl_coolkey_object_t *)attribute->object);
+		if (r < 0) {
+			return r;
+		}
+		obj = attribute->object->data;
+	}
+
+	/* should be a static assert so we catch this at compile time */
+	assert(sizeof(coolkey_object_header_t) >= sizeof(coolkey_v0_object_header_t));
+	/* make sure we have enough of the object to read the record_type */
+	if (buf_len <= sizeof(coolkey_v0_object_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	object_record_type = object_head->record_type;
+	/* make sure it's a type we recognize */
+	if ((object_record_type != COOLKEY_V1_OBJECT) && (object_record_type != COOLKEY_V0_OBJECT)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+
+
+	/*
+	 * now loop through all the attributes in the list. first find the start of the list
+	 */
+	attr = coolkey_attribute_start(obj, object_record_type, buf_len);
+	if (attr == NULL) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	buf_len -= (attr-obj);
+
+	/* now get the count */
+	attribute_count = coolkey_get_attribute_count(obj, object_record_type, buf_len);
+	for (i=0; i < attribute_count; i++) {
+		size_t record_len = coolkey_get_attribute_record_len(attr, object_record_type, buf_len);
+		/* make sure we have the complete record */
+		if (buf_len < record_len) {
+				return SC_ERROR_CORRUPTED_DATA;
+		}
+		/* does the attribute match the one we are looking for */
+		if (attr_type == coolkey_get_attribute_type(attr, object_record_type, record_len)) {
+			/* yup, return it */
+			return coolkey_get_attribute_data(attr, object_record_type, record_len, attribute);
+		}
+		/* go to the next attribute on the list */
+		buf_len -= record_len;
+		attr += record_len;
+	}
+	/* not find in attribute list, check the fixed attribute record */
+	if (object_record_type == COOLKEY_V1_OBJECT) {
+		unsigned long fixed_attributes = bebytes2ulong(object_head->fixed_attributes_values);
+
+		return coolkey_get_attribute_data_fixed(attr_type, fixed_attributes, attribute);
+	}
+	return SC_ERROR_DATA_OBJECT_NOT_FOUND;
+}
+
+/*
+ * pkcs 15 needs to find the cert matching the keys to fill in some of the fields that wasn't stored
+ * with the key. To do this we need to look for the cert matching the key's CKA_ID. For flexibility,
+ * We simply search using a pkcs #11 style template using the cardctl_coolkey_attribute_t structure */
+sc_cardctl_coolkey_object_t *
+coolkey_find_object_by_template(sc_card_t *card, sc_cardctl_coolkey_attribute_t *template, int count)
+{
+	list_t *list;
+	sc_cardctl_coolkey_object_t *current, *rv = NULL;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	int i, r;
+	unsigned int tmp_pos = (unsigned int) -1;
+
+	list = &priv->objects_list;
+	if (list->iter_active) {
+		/* workaround missing functionality of second iterator */
+		tmp_pos = list->iter_pos;
+		list_iterator_stop(list);
+	}
+
+	list_iterator_start(list);
+	while (list_iterator_hasnext(list)) {
+		sc_cardctl_coolkey_attribute_t attribute;
+		current = list_iterator_next(list);
+		attribute.object = current;
+
+		for (i=0; i < count; i++) {
+			attribute.attribute_type = template[i].attribute_type;
+			r = coolkey_find_attribute(card, &attribute);
+			if (r < 0) {
+				break;
+			}
+			if (template[i].attribute_data_type != attribute.attribute_data_type) {
+				break;
+			}
+			if (template[i].attribute_length != attribute.attribute_length) {
+				break;
+			}
+			if (memcmp(attribute.attribute_value, template[i].attribute_value,
+							attribute.attribute_length) != 0) {
+				break;
+			}
+		}
+		/* just return the first one */
+		if (i == count) {
+			rv = current;
+			break;
+		}
+	}
+
+	list_iterator_stop(list);
+	if (tmp_pos != (unsigned int)-1) {
+		/* workaround missing functionality of second iterator */
+		list_iterator_start(list);
+		while (list_iterator_hasnext(list) && list->iter_pos < tmp_pos)
+			(void) list_iterator_next(list);
+	}
+	return rv;
+}
+
+static int
+coolkey_find_object(sc_card_t *card, sc_cardctl_coolkey_find_object_t *fobj)
+{
+	sc_cardctl_coolkey_object_t *obj = NULL;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	int r;
+
+	switch (fobj->type) {
+	case SC_CARDCTL_COOLKEY_FIND_BY_ID:
+		obj = coolkey_find_object_by_id(&priv->objects_list, fobj->find_id);
+		break;
+	case SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE:
+		obj = coolkey_find_object_by_template(card, fobj->coolkey_template, fobj->template_count);
+		break;
+	default:
+		break;
+	}
+	if (obj == NULL) {
+		return SC_ERROR_DATA_OBJECT_NOT_FOUND;
+	}
+	if (obj->data == NULL) {
+		r = coolkey_fill_object(card, obj);
+		if (r < 0) {
+			return r;
+		}
+	}
+	fobj->obj = obj;
+	return SC_SUCCESS;
+}
+
+static int coolkey_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr)
+{
+	coolkey_private_data_t * priv = COOLKEY_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 coolkey_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr);
+		case SC_CARDCTL_COOLKEY_GET_TOKEN_INFO:
+			return coolkey_get_token_info(card, (sc_pkcs15_tokeninfo_t *) ptr);
+		case SC_CARDCTL_COOLKEY_FIND_OBJECT:
+			return coolkey_find_object(card, (sc_cardctl_coolkey_find_object_t *)ptr);
+		case SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS:
+			return coolkey_get_init_and_get_count(&priv->objects_list, (int *)ptr);
+		case SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT:
+			return coolkey_fetch_object(&priv->objects_list, (sc_cardctl_coolkey_object_t *)ptr);
+		case SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS:
+			return coolkey_final_iterator(&priv->objects_list);
+		case SC_CARDCTL_COOLKEY_GET_ATTRIBUTE:
+			return coolkey_find_attribute(card,(sc_cardctl_coolkey_attribute_t *)ptr);
+	}
+
+	LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+}
+
+static int coolkey_get_challenge(sc_card_t *card, u8 *rnd, size_t len)
+{
+	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);
+
+	for(; len >= COOLKEY_MAX_CHUNK_SIZE; len -= COOLKEY_MAX_CHUNK_SIZE,
+										rnd += COOLKEY_MAX_CHUNK_SIZE) {
+		rbuflen = COOLKEY_MAX_CHUNK_SIZE;
+		r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM,
+			0, 0, NULL, 0, &rnd, &rbuflen,  NULL, 0);
+		if (r != COOLKEY_MAX_CHUNK_SIZE) {
+			len = 0;
+			break;
+		}
+	}
+	if (len) {
+		r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM,
+			0, 0, NULL, 0, &rnd, &len, NULL, 0);
+	}
+	sc_unlock(card);
+
+	if (r > 0) {
+		r= SC_SUCCESS;
+	}
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+}
+
+static int coolkey_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num)
+{
+	int r = SC_SUCCESS;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+
+	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) && (env->algorithm != SC_ALGORITHM_EC)) {
+		 r = SC_ERROR_NO_CARD_SUPPORT;
+	}
+	priv->algorithm = env->algorithm;
+	priv->operation = env->operation;
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
+}
+
+
+static int coolkey_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);
+}
+
+#define MAX_COMPUTE_BUF 200
+typedef struct coolkey_compute_crypt_init_params {
+	u8 mode;
+	u8 direction;
+	u8 location;
+	u8 buf_len[2];
+} coolkey_compute_crypt_init_params_t;
+
+typedef struct coolkey_compute_crypt_params {
+    coolkey_compute_crypt_init_params_t init;
+	u8 buf[MAX_COMPUTE_BUF];
+} coolkey_compute_crypt_params_t;
+
+typedef struct coolkey_compute_ecc_params {
+	u8 location;
+	u8 buf_len[2];
+	u8 buf[MAX_COMPUTE_BUF];
+} coolkey_compute_ecc_params_t;
+
+static int coolkey_rsa_op(sc_card_t *card,
+					const u8 * data, size_t datalen,
+					u8 * out, size_t max_out_len)
+{
+	int r;
+	const u8 *crypt_in;
+	u8 **crypt_out_p;
+	size_t crypt_in_len, *crypt_out_len_p;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	coolkey_compute_crypt_params_t params;
+	u8 key_number;
+	size_t params_len;
+	size_t buf_len;
+	u8 buf[MAX_COMPUTE_BUF+2];
+	u8 *buf_out;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"datalen=%d outlen=%d\n", datalen, max_out_len);
+
+	crypt_in = data;
+	crypt_in_len = datalen;
+
+	buf_out = &buf[0];
+	crypt_out_p = &buf_out;
+	buf_len = sizeof(buf);
+	crypt_out_len_p = &buf_len;
+	key_number = priv->key_id;
+	params.init.mode = COOLKEY_CRYPT_MODE_RSA_NO_PAD;
+	params.init.location = COOLKEY_CRYPT_LOCATION_APDU;
+	params.init.direction = COOLKEY_CRYPT_DIRECTION_ENCRYPT; /* for no pad, direction is irrelevant */
+
+	if (priv->key_id > 0xff) {
+		r = SC_ERROR_NO_DEFAULT_KEY;
+		goto done;
+	}
+
+	params_len = sizeof(params.init) + crypt_in_len;
+
+	/* send the data to the card if necessary */
+	if (crypt_in_len > MAX_COMPUTE_BUF) {
+		u8 len_buf[2];
+		params.init.location = COOLKEY_CRYPT_LOCATION_DL_OBJECT;
+		params_len = sizeof(params.init);
+		crypt_in = NULL;
+		crypt_in_len = 0;
+		*crypt_out_p = NULL;
+		*crypt_out_len_p = 0;
+
+		ushort2bebytes(len_buf, datalen);
+
+		r = coolkey_write_object(card, COOLKEY_DL_OBJECT_ID, 0, len_buf, sizeof(len_buf),
+					priv->nonce, sizeof(priv->nonce));
+		if (r < 0) {
+			goto done;
+		}
+
+		r = coolkey_write_object(card, COOLKEY_DL_OBJECT_ID, 2, data, datalen, priv->nonce,
+						sizeof(priv->nonce));
+		if (r < 0) {
+			goto done;
+		}
+
+	}
+	ushort2bebytes(params.init.buf_len, crypt_in_len);
+	if (crypt_in_len) {
+		memcpy(params.buf, crypt_in, crypt_in_len);
+	}
+
+
+	r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_COMPUTE_CRYPT,
+			key_number, COOLKEY_CRYPT_ONE_STEP, (u8 *)&params, params_len,
+			crypt_out_p, crypt_out_len_p, priv->nonce, sizeof(priv->nonce));
+
+	if (r < 0) {
+		goto done;
+	}
+	if (datalen > MAX_COMPUTE_BUF) {
+		u8 len_buf[2];
+		size_t out_length;
+
+		r = coolkey_read_object(card, COOLKEY_DL_OBJECT_ID, 0, len_buf, sizeof(len_buf),
+					priv->nonce, sizeof(priv->nonce));
+		if (r < 0) {
+			goto done;
+		}
+
+		out_length = bebytes2ushort(len_buf);
+		out_length = MIN(out_length,max_out_len);
+
+		r = coolkey_read_object(card, COOLKEY_DL_OBJECT_ID, sizeof(len_buf), out, out_length,
+					priv->nonce, sizeof(priv->nonce));
+
+	} else {
+		size_t out_length = bebytes2ushort(buf);
+		out_length = MIN(out_length, max_out_len);
+		memcpy(out, buf+2, out_length);
+		r = out_length;
+	}
+
+done:
+	return r;
+}
+
+static int coolkey_ecc_op(sc_card_t *card,
+					const u8 * data, size_t datalen,
+					u8 * out, size_t outlen)
+{
+	int r;
+	const u8 *crypt_in;
+	u8  **crypt_out_p;
+	u8  ins = 0;
+	size_t crypt_in_len, *crypt_out_len_p;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	coolkey_compute_ecc_params_t params;
+	size_t params_len;
+	u8 key_number;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"datalen=%d outlen=%d\n", datalen, outlen);
+
+	crypt_in = data;
+	crypt_in_len = datalen;
+
+	crypt_out_p = &out;
+	crypt_out_len_p = &outlen;
+	key_number = priv->key_id;
+	params.location = COOLKEY_CRYPT_LOCATION_APDU;
+
+	if (priv->key_id > 0xff) {
+		r = SC_ERROR_NO_DEFAULT_KEY;
+		goto done;
+	}
+
+	switch (priv->operation) {
+	case SC_SEC_OPERATION_DERIVE:
+		ins = COOLKEY_INS_COMPUTE_ECC_KEY_AGREEMENT;
+		break;
+	case SC_SEC_OPERATION_SIGN:
+		ins = COOLKEY_INS_COMPUTE_ECC_SIGNATURE;
+		break;
+	default:
+		r = SC_ERROR_NOT_SUPPORTED;
+		goto done;
+	}
+
+	params_len = (sizeof(params) - sizeof(params.buf))  + crypt_in_len;
+
+	ushort2bebytes(params.buf_len, crypt_in_len);
+	if (crypt_in_len) {
+		memcpy(params.buf, crypt_in, crypt_in_len);
+	}
+
+
+	r = coolkey_apdu_io(card, COOLKEY_CLASS, ins,
+			key_number, COOLKEY_CRYPT_ONE_STEP, (u8 *)&params, params_len,
+			crypt_out_p, crypt_out_len_p, priv->nonce, sizeof(priv->nonce));
+
+done:
+	return r;
+}
+
+
+static int coolkey_compute_crypt(sc_card_t *card,
+					const u8 * data, size_t datalen,
+					u8 * out, size_t outlen)
+{
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	int r;
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	switch (priv->algorithm) {
+	case SC_ALGORITHM_RSA:
+		r = coolkey_rsa_op(card, data, datalen, out, outlen);
+		break;
+	case SC_ALGORITHM_EC:
+		r = coolkey_ecc_op(card, data, datalen, out, outlen);
+		break;
+	default:
+		r = SC_ERROR_NO_CARD_SUPPORT;
+		break;
+	}
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
+}
+
+
+static u8 coolkey_class(unsigned long object_id) {
+	return (object_id >> 24) & 0xff;
+}
+
+static unsigned short coolkey_get_key_id(unsigned long object_id) {
+	char char_index = (object_id >> 16) & 0xff;
+	if (char_index >= '0' && char_index <= '9') {
+		return (u8)(char_index - '0');
+	}
+	if (char_index >= 'A' && char_index <= 'Z') {
+		return (u8)(char_index - 'A' + 10);
+	}
+	if (char_index >= 'a' && char_index <= 'z') {
+		return (u8)(char_index - 'a' + 26 + 10);
+	}
+	return COOLKEY_INVALID_KEY;
+}
+
+/*
+ * COOLKEY cards don't select objects in the applet, objects are selected by a paramter
+ * to the APDU. We create paths for the object in which the path value is the object_id
+ * and the path type is SC_PATH_SELECT_FILE_ID (so we could cache at the PKCS #15 level if
+ * we wanted to.
+ *
+ * This select simply records what object was selected so that read knows how to access it.
+ */
+static int coolkey_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
+{
+	int r;
+	struct sc_file *file = NULL;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	unsigned long object_id;
+
+	assert(card != NULL && in_path != NULL);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	if (in_path->len != 4) {
+		return SC_ERROR_OBJECT_NOT_FOUND;
+	}
+	r = coolkey_select_applet(card);
+	if (r != SC_SUCCESS) {
+		return r;
+	}
+	object_id = bebytes2ulong(in_path->value);
+	priv->obj = coolkey_find_object_by_id(&priv->objects_list, object_id);
+	if (priv->obj == NULL) {
+		return SC_ERROR_OBJECT_NOT_FOUND;
+	}
+
+	priv->key_id = COOLKEY_INVALID_KEY;
+	if (coolkey_class(object_id) == COOLKEY_KEY_CLASS) {
+		priv->key_id = coolkey_get_key_id(object_id);
+	}
+	if (file_out) {
+		file = sc_file_new();
+		if (file == NULL)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY);
+		file->path = *in_path;
+		/* this could be like the FCI */
+		file->type =  SC_PATH_TYPE_FILE_ID;
+		file->shareable = 0;
+		file->ef_structure = 0;
+		file->size = priv->obj->length;
+	}
+
+    return SC_SUCCESS;
+}
+
+static int coolkey_finish(sc_card_t *card)
+{
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+	if (priv) {
+		coolkey_free_private_data(priv);
+	}
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_add_object(coolkey_private_data_t *priv, unsigned long object_id, const u8 *object_data, size_t object_length, int add_v1_record)
+{
+	sc_cardctl_coolkey_object_t new_object;
+	int r;
+
+	memset(&new_object, 0, sizeof(new_object));
+	new_object.path = coolkey_template_path;
+	new_object.path.len = 4;
+	ulong2bebytes(new_object.path.value, object_id);
+	new_object.id = object_id;
+	new_object.length = object_length;
+
+	if (object_data) {
+		new_object.data = malloc(object_length + add_v1_record);
+		if (new_object.data == NULL) {
+			return SC_ERROR_OUT_OF_MEMORY;
+		}
+		if (add_v1_record) {
+			new_object.data[0] = COOLKEY_V1_OBJECT;
+			new_object.length++;
+		}
+		memcpy(&new_object.data[add_v1_record], object_data, object_length);
+	}
+
+	r = coolkey_add_object_to_list(&priv->objects_list, &new_object);
+	if (r != SC_SUCCESS) {
+		/* if we didn't successfully put the object on the list,
+		 * the data space didn't get adopted. free it before we return */
+		free(new_object.data);
+		new_object.data = NULL;
+	}
+	return r;
+}
+
+
+static int
+coolkey_process_combined_object(sc_card_t *card, coolkey_private_data_t *priv, u8 *object, size_t object_length)
+{
+	coolkey_combined_header_t *header = (coolkey_combined_header_t *)object;
+	unsigned short compressed_offset;
+	unsigned short compressed_length;
+	unsigned short compressed_type;
+	unsigned short object_offset;
+	unsigned short object_count;
+	coolkey_decompressed_header_t *decompressed_header;
+	u8 *decompressed_object = NULL;
+	size_t decompressed_object_len = 0;
+	int free_decompressed = 0;
+	int i, r;
+
+	if (object_length < sizeof(coolkey_combined_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	compressed_offset = bebytes2ushort(header->compression_offset);
+	compressed_length = bebytes2ushort(header->compression_length);
+	compressed_type   = bebytes2ushort(header->compression_type);
+
+	if ((((size_t)compressed_offset) + (size_t)compressed_length) >  object_length) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+
+	/* store the CUID */
+	memcpy(&priv->cuid, &header->cuid, sizeof(priv->cuid));
+
+	if (compressed_type == COOLKEY_COMPRESSION_ZLIB) {
+#ifdef ENABLE_ZLIB
+		r = sc_decompress_alloc(&decompressed_object, &decompressed_object_len, &object[compressed_offset], compressed_length, COMPRESSION_AUTO);
+		if (r)
+			goto done;
+		free_decompressed = 1;
+#else
+		sc_log(card->ctx, "Coolkey compression not supported, no zlib");
+		LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED);
+#endif
+	}  else {
+		decompressed_object =&object[compressed_offset];
+		decompressed_object_len = (size_t) compressed_length;
+	}
+
+	decompressed_header = (coolkey_decompressed_header_t *)decompressed_object;
+
+	if (decompressed_object_len < sizeof(coolkey_decompressed_header_t)) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	object_offset = bebytes2ushort(decompressed_header->object_offset);
+	object_count = bebytes2ushort(decompressed_header->object_count);
+
+
+	/*
+	 * using 2 different tests here so we can log different errors if logging is
+	 * turned on.
+	 */
+	/* make sure token_name doesn't overrun the buffer */
+	if (decompressed_header->token_name_length +
+		offsetof(coolkey_decompressed_header_t,token_name) > decompressed_object_len) {
+		r = SC_ERROR_CORRUPTED_DATA;
+		goto done;
+	}
+	/* make sure it doesn't overlap the object space */
+	if (decompressed_header->token_name_length +
+		offsetof(coolkey_decompressed_header_t,token_name) > object_offset) {
+		r = SC_ERROR_CORRUPTED_DATA;
+		goto done;
+	}
+
+	/* store the token name in the priv structure so the emulator can set it */
+	priv->token_name = malloc(decompressed_header->token_name_length+1);
+	if (priv->token_name == NULL) {
+		r = SC_ERROR_OUT_OF_MEMORY;
+		goto done;
+	}
+	memcpy(priv->token_name, &decompressed_header->token_name[0],
+							decompressed_header->token_name_length);
+	priv->token_name[decompressed_header->token_name_length] = 0;
+	priv->token_name_length = decompressed_header->token_name_length;
+
+
+	for (i=0; i < object_count && object_offset < decompressed_object_len; i++ ) {
+		u8 *current_object = &decompressed_object[object_offset];
+		coolkey_combined_object_header_t *object_header =
+				(coolkey_combined_object_header_t *)current_object;
+		unsigned long object_id = bebytes2ulong(object_header->object_id);
+		int current_object_len;
+
+		/* figure out how big it is */
+		r = coolkey_v1_get_object_length(current_object, decompressed_object_len-object_offset);
+		if (r < 0) {
+			goto done;
+		}
+		if ((size_t)r + object_offset > decompressed_object_len) {
+			r = SC_ERROR_CORRUPTED_DATA;
+			goto done;
+		}
+		current_object_len = r;
+		object_offset += current_object_len;
+
+		/* record this object */
+		r = coolkey_add_object(priv, object_id, current_object, current_object_len, 1);
+		if (r) {
+			goto done;
+		}
+
+	}
+	r = SC_SUCCESS;
+
+done:
+	if (free_decompressed) {
+		free(decompressed_object);
+	}
+	return r;
+}
+
+static int
+coolkey_list_object(sc_card_t *card, u8 seq, coolkey_object_info_t *object_info)
+{
+	u8 *rbuf = (u8 *) object_info;
+	size_t rbuflen = sizeof(*object_info);
+
+	return coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_LIST_OBJECTS, seq, 0,
+			NULL, 0, &rbuf, &rbuflen, NULL, 0);
+
+}
+
+/*
+ * Initialize the Coolkey data structures.
+ */
+static int coolkey_initialize(sc_card_t *card)
+{
+	int r;
+	coolkey_private_data_t *priv = NULL;
+	coolkey_life_cycle_t life_cycle;
+	coolkey_object_info_t object_info;
+	int combined_processed = 0;
+
+	/* already found? */
+	if (card->drv_data) {
+		return SC_SUCCESS;
+	}
+
+	/* Select a coolkey read the coolkey objects out */
+	r = coolkey_select_applet(card);
+	if (r < 0) {
+		return r;
+	}
+	sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"Coolkey Applet found");
+
+	priv = coolkey_new_private_data();
+	if (priv == NULL) {
+		r= SC_ERROR_OUT_OF_MEMORY;
+		goto cleanup;
+	}
+	r = coolkey_get_life_cycle(card, &life_cycle);
+	if (r < 0) {
+		goto cleanup;
+	}
+	priv->protocol_version_major = life_cycle.protocol_version_major;
+	priv->protocol_version_minor = life_cycle.protocol_version_minor;
+	priv->pin_count = life_cycle.pin_count;
+	priv->life_cycle = life_cycle.life_cycle;
+
+	/* walk down the list of objects and read them off the token */
+	for(r=coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info); r >= 0;
+		r= coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info)) {
+		unsigned long object_id = bebytes2ulong(object_info.object_id);
+		unsigned short object_len = bebytes2ulong(object_info.object_length);
+	    /* also look at the ACL... */
+
+
+		/* the combined object is a single object that can store the other objects.
+		 * most coolkeys provisioned by TPS has a single combined object that is
+		 * compressed greatly increasing the effectiveness of compress (since lots
+		 * of certs on the token share the same Subject and Issuer DN's). We now
+		 * process it separately so that we can have both combined objects managed
+		 * by TPS and user managed certs on the same token */
+		if (object_id == COOLKEY_COMBINED_OBJECT_ID) {
+			u8 *object = malloc(object_len);
+			if (object == NULL) {
+				r = SC_ERROR_OUT_OF_MEMORY;
+				break;
+			}
+			r = coolkey_read_object(card, COOLKEY_COMBINED_OBJECT_ID, 0, object, object_len,
+											priv->nonce, sizeof(priv->nonce));
+			if (r < 0) {
+				free(object);
+				break;
+			}
+			r = coolkey_process_combined_object(card, priv, object, r);
+			free(object);
+			if (r != SC_SUCCESS) {
+				break;
+			}
+			combined_processed = 1;
+			continue;
+		}
+		r = coolkey_add_object(priv, object_id, NULL, object_len, 0);
+
+	}
+	if (r != SC_ERROR_FILE_END_REACHED) {
+		goto cleanup;
+	}
+	/* if we didn't pull the cuid from the combined object, then grab it now */
+	if (!combined_processed) {
+		global_platform_cplc_data_t cplc_data;
+		r = coolkey_get_cplc_data(card, &cplc_data);
+		if (r < 0) {
+			goto cleanup;
+		}
+		coolkey_make_cuid_from_cplc(&priv->cuid, &cplc_data);
+		priv->token_name = (u8 *)strdup("COOLKEY");
+		if (priv->token_name == NULL) {
+			r= SC_ERROR_OUT_OF_MEMORY;
+			goto cleanup;
+		}
+		priv->token_name_length = sizeof("COOLKEY")-1;
+	}
+	card->drv_data = priv;
+	return SC_SUCCESS;
+
+cleanup:
+	if (priv) {
+		coolkey_free_private_data(priv);
+	}
+	return r;
+}
+
+
+/* NOTE: returns a bool, 1 card matches, 0 it does not */
+static int coolkey_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 = coolkey_select_applet(card);
+	return (r >= SC_SUCCESS);
+}
+
+
+static int coolkey_init(sc_card_t *card)
+{
+	int r;
+	unsigned long flags;
+	unsigned long ext_flags;
+	coolkey_private_data_t * priv;
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	r = coolkey_initialize(card);
+	if (r < 0) {
+		SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+	}
+
+	card->type = SC_CARD_TYPE_COOLKEY_GENERIC;
+
+	/* set Token Major/minor version */
+	flags = SC_ALGORITHM_RSA_RAW;
+
+	_sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */
+	_sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
+	_sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
+
+	flags = SC_ALGORITHM_ECDSA_RAW | SC_ALGORITHM_ECDH_CDH_RAW | SC_ALGORITHM_ECDSA_HASH_NONE;
+	ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES;
+
+	_sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL);
+	_sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL);
+	_sc_card_add_ec_alg(card, 521, flags, ext_flags, NULL);
+
+
+	priv = COOLKEY_DATA(card);
+	if (priv->pin_count != 0) {
+		card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO;
+	}
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+static int
+coolkey_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
+{
+	int r;
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	size_t rbuflen;
+	u8 *rbuf;
+
+	/* COOLKEY uses a separate pin from the card pin, managed by the applet.
+	 * if we successfully log into coolkey, we will get a nonce, which we append
+	 * to our APDUs to authenticate the apdu to the card. This allows coolkey to
+	 * maintain separate per application login states without the application
+	 * having to cache the pin */
+	switch (data->cmd) {
+	case SC_PIN_CMD_GET_INFO:
+		if (priv->nonce_valid) {
+			data->pin1.logged_in = SC_PIN_STATE_LOGGED_IN;
+		} else {
+			data->pin1.logged_in = SC_PIN_STATE_LOGGED_OUT;
+			/* coolkey retries is 100. It's unlikely the pin is block.
+			 * instead, coolkey slows down the login command exponentially
+			 */
+			data->pin1.tries_left = 0xf;
+		}
+		if (tries_left) {
+			*tries_left = data->pin1.tries_left;
+		}
+		r = SC_SUCCESS;
+		break;
+
+	case SC_PIN_CMD_UNBLOCK:
+	case SC_PIN_CMD_CHANGE:
+		/* these 2 commands are currently reserved for TPS */
+	default:
+		r = SC_ERROR_NOT_SUPPORTED;
+		break;
+	case SC_PIN_CMD_VERIFY:
+		/* coolkey applet supports multiple pins, but TPS currently only uses one.
+		 * just support the one pin for now (we need an array of nonces to handle
+		 * multiple pins) */
+		/* coolkey only supports unpadded ascii pins, so no need to format the pin */
+		rbuflen = sizeof(priv->nonce);
+		rbuf = &priv->nonce[0];
+		r = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_VERIFY_PIN,
+			data->pin_reference, 0, data->pin1.data, data->pin1.len,
+			&rbuf, &rbuflen, NULL, 0);
+		if (r < 0) {
+			break;
+		}
+		priv->nonce_valid = 1;
+		r = SC_SUCCESS;
+	}
+	return r;
+}
+
+static int
+coolkey_logout(sc_card_t *card)
+{
+	/* when we add multi pin support here, how do we know which pin to logout? */
+	coolkey_private_data_t * priv = COOLKEY_DATA(card);
+	u8 pin_ref = 0;
+
+	(void) coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_LOGOUT, pin_ref, 0, NULL, 0, NULL, NULL,
+		priv->nonce, sizeof(priv->nonce));
+	/* even if logout failed on the card, flush the nonce and clear the nonce_valid and we are effectively
+	 * logged out... needing to login again to get a nonce back */
+	memset(priv->nonce, 0, sizeof(priv->nonce));
+	priv->nonce_valid = 0;
+	return SC_SUCCESS;
+}
+
+static struct sc_card_operations coolkey_ops;
+
+static struct sc_card_driver coolkey_drv = {
+	"COOLKEY",
+	"coolkey",
+	&coolkey_ops,
+	NULL, 0, NULL
+};
+
+static struct sc_card_driver * sc_get_driver(void)
+{
+	struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
+
+	coolkey_ops = *iso_drv->ops;
+	coolkey_ops.match_card = coolkey_match_card;
+	coolkey_ops.init = coolkey_init;
+	coolkey_ops.finish = coolkey_finish;
+
+	coolkey_ops.select_file =  coolkey_select_file; /* need to record object type */
+	coolkey_ops.get_challenge = coolkey_get_challenge;
+	coolkey_ops.read_binary = coolkey_read_binary;
+	coolkey_ops.write_binary = coolkey_write_binary;
+	coolkey_ops.set_security_env = coolkey_set_security_env;
+	coolkey_ops.restore_security_env = coolkey_restore_security_env;
+	coolkey_ops.compute_signature = coolkey_compute_crypt;
+	coolkey_ops.decipher =  coolkey_compute_crypt;
+	coolkey_ops.card_ctl = coolkey_card_ctl;
+	coolkey_ops.check_sw = coolkey_check_sw;
+	coolkey_ops.pin_cmd = coolkey_pin_cmd;
+	coolkey_ops.logout = coolkey_logout;
+
+	return &coolkey_drv;
+}
+
+
+struct sc_card_driver * sc_get_coolkey_driver(void)
+{
+	return sc_get_driver();
+}
+
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index 6f1f68d..22c654c 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -219,6 +219,17 @@ enum {
         SC_CARDCTL_AUTHENTIC_SDO_STORE,
         SC_CARDCTL_AUTHENTIC_SDO_GENERATE,
 
+	/*
+	 * Coolkey specific calls
+	 */
+	SC_CARDCTL_COOLKEY_BASE = _CTL_PREFIX('C', 'O', 'K'),
+	SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS,
+	SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT,
+	SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS,
+	SC_CARDCTL_COOLKEY_GET_ATTRIBUTE,
+	SC_CARDCTL_COOLKEY_GET_TOKEN_INFO,
+	SC_CARDCTL_COOLKEY_FIND_OBJECT,
+
         /*
 	 * IAS/ECC
 	 */
@@ -1031,6 +1042,40 @@ typedef struct sc_cardctl_isoApplet_import_key {
 	} privkey;
 } sc_cardctl_isoApplet_import_key_t;
 
+/*
+ * coolkey object returned from the card control interface
+ */
+typedef struct sc_cardctl_coolkey_object {
+        sc_path_t path;
+        unsigned long id;
+        size_t length;
+        u8  *data;
+} sc_cardctl_coolkey_object_t;
+
+
+/* data structure to pass attributes through the ctl interface */
+typedef struct sc_cardctl_coolkey_attribute {
+        const sc_cardctl_coolkey_object_t *object;
+	unsigned long attribute_type;
+        u8 attribute_data_type;
+        size_t attribute_length;
+        const u8 *attribute_value;
+} sc_cardctl_coolkey_attribute_t;
+
+#define SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING 0
+#define SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG 1
+
+typedef struct sc_cardctl_coolkey_find_object {
+	int type; /* in parameter */
+	unsigned long find_id; /* in parameter */
+	sc_cardctl_coolkey_attribute_t *coolkey_template; /* in paramter */
+	int template_count;                       /* in parameter */
+	sc_cardctl_coolkey_object_t *obj; /* out parameter */
+} sc_cardctl_coolkey_find_object_t;
+
+#define SC_CARDCTL_COOLKEY_FIND_BY_ID       0
+#define SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE 1
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index 5c8fb40..7ab94bc 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -231,6 +231,10 @@ enum {
 
 	/* JPKI cards */
 	SC_CARD_TYPE_JPKI_BASE = 31000,
+
+	/* CAC cards */
+	SC_CARD_TYPE_COOLKEY_BASE = 32000,
+	SC_CARD_TYPE_COOLKEY_GENERIC,
 };
 
 extern sc_card_driver_t *sc_get_default_driver(void);
@@ -271,6 +275,7 @@ extern sc_card_driver_t *sc_get_isoApplet_driver(void);
 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);
 
 #ifdef __cplusplus
 }
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index bdcb5ab..0aaad09 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -119,6 +119,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
 #endif
 	{ "openpgp",	(void *(*)(void)) sc_get_openpgp_driver },
 	{ "jpki",	(void *(*)(void)) sc_get_jpki_driver },
+	{ "coolkey",	(void *(*)(void)) sc_get_coolkey_driver },
 	/* The default driver should be last, as it handles all the
 	 * unrecognized cards. */
 	{ "default",	(void *(*)(void)) sc_get_default_driver },
diff --git a/src/libopensc/pkcs15-coolkey.c b/src/libopensc/pkcs15-coolkey.c
new file mode 100644
index 0000000..e96def5
--- /dev/null
+++ b/src/libopensc/pkcs15-coolkey.c
@@ -0,0 +1,695 @@
+/*
+ * partial PKCS15 emulation for Coolkey cards
+ *
+ * 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.
+ *
+ * Coolkey 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 "asn1.h"
+#include "pkcs15.h"
+#include "../pkcs11/pkcs11.h"
+
+int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *, 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 coolkey_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_COOLKEY_GENERIC
+		|| card->type >= SC_CARD_TYPE_COOLKEY_GENERIC+1000)
+		return SC_ERROR_INVALID_CARD;
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_get_object(sc_card_t *card, unsigned long object_id, sc_cardctl_coolkey_object_t **obj) {
+	sc_cardctl_coolkey_find_object_t fobj;
+	int r;
+
+	fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_ID;
+	fobj.find_id = object_id;
+	fobj.obj = NULL;
+	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj);
+	if (r < 0) {
+		return r;
+	}
+	*obj = fobj.obj;
+	return SC_SUCCESS;
+}
+
+
+/*
+ * fetch attributes from an object
+ */
+static int
+coolkey_get_attribute(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, const u8 **val, size_t *val_len, u8 *data_type) {
+	sc_cardctl_coolkey_attribute_t attribute;
+	int r;
+
+	attribute.object = obj;
+	attribute.attribute_type = type;
+
+	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &attribute);
+	if (r < 0) {
+		return r;
+	}
+	*val = attribute.attribute_value;
+	*val_len = attribute.attribute_length;
+	if (data_type) {
+		*data_type = attribute.attribute_data_type;
+	}
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_find_matching_cert(sc_card_t *card, sc_cardctl_coolkey_object_t *in_obj, sc_cardctl_coolkey_object_t **cert_obj) {
+	sc_cardctl_coolkey_find_object_t fobj;
+	sc_cardctl_coolkey_attribute_t template[2];
+	u8 obj_class[4];
+	int r;
+
+	/* we are searching for certs .. */
+	template[0].attribute_type = CKA_CLASS;
+	template[0].attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG;
+	template[0].attribute_length = sizeof(obj_class);
+	template[0].attribute_value = obj_class;
+	ulong2bebytes(obj_class, CKO_CERTIFICATE);
+
+	/* fetch the current object's CKA_ID */
+	template[1].attribute_type = CKA_ID;
+	template[1].object = in_obj;
+	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &template[1]);
+	if (r < 0) {
+		return r;
+	}
+	template[0].object = NULL; /*paranoia */
+	template[1].object = NULL; /*paranoia */
+
+	/* now find the cert that has the ID */
+	fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE;
+	fobj.obj = NULL;
+	fobj.coolkey_template = &template[0];
+	fobj.template_count=2;
+	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj);
+	if (r < 0) {
+		return r;
+	}
+	*cert_obj = fobj.obj;
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_get_attribute_ulong(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
+{
+	const u8 *val;
+	size_t val_len;
+	u8 data_type;
+	int r;
+
+	r  = coolkey_get_attribute(card, obj, type, &val, &val_len, &data_type);
+	if (r < 0) {
+		return r;
+	}
+	if ((data_type != SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG) &&
+	    (val_len != sizeof(CK_ULONG))) {
+		return SC_ERROR_CORRUPTED_DATA;
+	}
+	*value = bebytes2ulong(val);
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_get_attribute_boolean(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE attr_type)
+{
+	int r;
+	const u8 *val;
+	size_t val_len;
+
+	r = coolkey_get_attribute(card, obj, attr_type, &val, &val_len, NULL);
+	if (r < 0) {
+		/* attribute not valid for this object, set boolean to false */
+		return 0;
+	}
+	if ((val_len == 1) && (*val == 1)) {
+		return 1;
+	}
+	return 0;
+}
+
+static int
+coolkey_get_attribute_bytes(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 *data, size_t *data_len, size_t max_data_len)
+{
+	const u8 *val;
+	size_t val_len;
+	int r;
+
+	r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL);
+	if (r < 0) {
+		return r;
+	}
+	if (val_len > max_data_len) {
+		val_len = max_data_len;
+	}
+	memcpy(data, val, val_len);
+	*data_len = val_len;
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_get_attribute_bytes_alloc(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 **data, size_t *data_len)
+{
+	const u8 *val;
+	size_t val_len;
+	int r;
+
+	r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL);
+	if (r < 0) {
+		return r;
+	}
+	*data = malloc(val_len);
+	if (*data == NULL) {
+		return SC_ERROR_OUT_OF_MEMORY;
+	}
+	memcpy(*data, val, val_len);
+	*data_len = val_len;
+	return SC_SUCCESS;
+}
+
+static int
+coolkey_get_id(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_id *id)
+{
+	return coolkey_get_attribute_bytes(card, obj, CKA_ID, id->value , &id->len, sizeof(id->value));
+}
+
+/*
+ * A number of opensc structure have the same layout, use a common function to fill them
+ * int:
+ *     structure name      first parameter                 second parameter
+ *     sc_lv_data          unsigned char * value           size_t len
+ *     sc_pkcs15_data         u8 *data                     size_t data_len
+ *     sc_pkcs15_bignum       u8 *data                     size_t len
+ *     sc_pkcs15_der          u8 *value	                   size_t len
+ *     sc_pkcs15_u8           u8 *value                    size_t len
+ *
+ * The following can properly assign all of them
+ */
+int
+coolkey_get_attribute_lv(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, void *ptr)
+{
+	struct sc_pkcs15_data *item = (struct sc_pkcs15_data *)ptr;
+	return coolkey_get_attribute_bytes_alloc(card, obj, type, &item->data, &item->data_len);
+}
+
+#define COOLKEY_ID_CERT ((unsigned long)'c')
+#define COOLKEY_ID_KEY ((unsigned long)'k')
+#define COOLKEY_ID_CERT_DATA ((unsigned long)'C')
+
+static unsigned long
+coolkey_get_object_type(unsigned long object_id) { return ((object_id >> 24 ) & 0xff); }
+
+static unsigned long
+coolkey_make_new_id(unsigned long object_id, unsigned long id_type)
+{ return ((object_id  & 0x00ffffffUL)|(id_type << 24)); }
+
+
+/*
+ * We need cert data to fill in some of our keys. Also, some older tokens store the cert data in a separate
+ * object from the rest of the cert attributes. This function handles both of these complications
+ */
+static int
+coolkey_get_certificate(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_der *cert)
+{
+	sc_cardctl_coolkey_object_t *cert_obj;
+	unsigned long object_id;
+	int r;
+
+	cert_obj = obj;
+	if (coolkey_get_object_type(obj->id) != COOLKEY_ID_CERT) {
+		r = coolkey_find_matching_cert(card, obj, &cert_obj);
+		if (r < 0) {
+			return r;
+		}
+	}
+	r = coolkey_get_attribute_lv(card, cert_obj, CKA_VALUE, cert);
+	if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
+		object_id = coolkey_make_new_id(cert_obj->id, COOLKEY_ID_CERT_DATA);
+		r = coolkey_get_object(card, object_id, &cert_obj);
+		if (r < 0) {
+			return r;
+		}
+		/* fill in cert data */
+		cert->value = malloc(cert_obj->length);
+		if (cert->value == NULL) {
+			return SC_ERROR_OUT_OF_MEMORY;
+		}
+		memcpy(cert->value, cert_obj->data, cert_obj->length);
+		cert->len = cert_obj->length;
+		return SC_SUCCESS;
+	}
+	return r;
+}
+
+
+
+struct coolkey_attr_flags {
+	CK_ATTRIBUTE_TYPE attribute_type;
+	unsigned int pkcs15_flags;
+};
+
+static struct coolkey_attr_flags usage_table[] = {
+	{ CKA_ENCRYPT,          SC_PKCS15_PRKEY_USAGE_ENCRYPT       },
+	{ CKA_DECRYPT,          SC_PKCS15_PRKEY_USAGE_DECRYPT       },
+	{ CKA_SIGN,             SC_PKCS15_PRKEY_USAGE_SIGN          },
+	{ CKA_SIGN_RECOVER,     SC_PKCS15_PRKEY_USAGE_SIGNRECOVER   },
+	{ CKA_WRAP,             SC_PKCS15_PRKEY_USAGE_WRAP          },
+	{ CKA_UNWRAP,           SC_PKCS15_PRKEY_USAGE_UNWRAP        },
+	{ CKA_VERIFY,           SC_PKCS15_PRKEY_USAGE_VERIFY        },
+	{ CKA_VERIFY_RECOVER,   SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER },
+	{ CKA_DERIVE,           SC_PKCS15_PRKEY_USAGE_DERIVE        }
+};
+
+static struct coolkey_attr_flags access_table[] = {
+	{ CKA_SENSITIVE,        SC_PKCS15_PRKEY_ACCESS_SENSITIVE       },
+    { CKA_EXTRACTABLE,      SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE     },
+    { CKA_ALWAYS_SENSITIVE, SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE },
+    { CKA_NEVER_EXTRACTABLE,SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE},
+    { CKA_LOCAL,            SC_PKCS15_PRKEY_ACCESS_LOCAL           }
+};
+
+static struct coolkey_attr_flags flag_table[] = {
+	{ CKA_PRIVATE,          SC_PKCS15_CO_FLAG_PRIVATE       },
+	{ CKA_MODIFIABLE,       SC_PKCS15_CO_FLAG_MODIFIABLE    }
+};
+
+static int usage_table_size = sizeof(usage_table)/sizeof(usage_table[0]);
+static int access_table_size = sizeof(access_table)/sizeof(access_table[0]);
+static int flag_table_size = sizeof(flag_table)/sizeof(flag_table[0]);
+
+static int
+coolkey_set_bool_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flags_ptr, struct coolkey_attr_flags *table, int table_size)
+{
+	unsigned int flags = 0;
+	int i;
+
+	for (i=0; i< table_size; i++) {
+		if (coolkey_get_attribute_boolean(card, obj, table[i].attribute_type)) {
+			flags |= table[i].pkcs15_flags;
+		}
+	}
+	*flags_ptr = flags;
+	return SC_SUCCESS;
+}
+
+/* map a cert usage and algorithm to public and private key usages */
+static int
+coolkey_get_usage(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *usage_ptr)
+{
+	return coolkey_set_bool_flags(card, obj, usage_ptr, usage_table, usage_table_size);
+}
+
+/* map a cert usage and algorithm to public and private key usages */
+static int
+coolkey_get_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flag_ptr)
+{
+	return coolkey_set_bool_flags(card, obj, flag_ptr, flag_table, flag_table_size);
+}
+
+static int
+coolkey_get_access(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *access_ptr)
+{
+	return coolkey_set_bool_flags(card, obj, access_ptr, access_table, access_table_size);
+}
+
+
+/*
+ * turn a coolkey object into a pkcss 15 pubkey. object should already be type
+ * CKO_PUBLIC_KEY */
+static sc_pkcs15_pubkey_t *
+coolkey_make_public_key(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type)
+{
+	sc_pkcs15_pubkey_t *key;
+	int r;
+
+	key = calloc(1, sizeof(struct sc_pkcs15_pubkey));
+	switch (key_type) {
+	case CKK_RSA:
+		key->algorithm = SC_ALGORITHM_RSA;
+		r = coolkey_get_attribute_lv(card, obj, CKA_MODULUS, &key->u.rsa.modulus);
+		if (r != SC_SUCCESS) {
+			goto fail;
+		}
+		r = coolkey_get_attribute_lv(card, obj, CKA_PUBLIC_EXPONENT, &key->u.rsa.exponent);
+		if (r != SC_SUCCESS) {
+			goto fail;
+		}
+		break;
+	case CKK_EC:
+		key->algorithm = SC_ALGORITHM_EC;
+		r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_POINT, &key->u.ec.ecpointQ.value, &key->u.ec.ecpointQ.len);
+	    if(r < 0) {
+			goto fail;
+		}
+		r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_PARAMS,
+				&key->u.ec.params.der.value, &key->u.ec.params.der.len);
+		if (r < 0) {
+			goto fail;
+		}
+		r = sc_pkcs15_fix_ec_parameters(card->ctx, &key->u.ec.params);
+		if (r < 0) {
+			goto fail;
+		}
+		break;
+	}
+	return key;
+fail:
+	sc_pkcs15_free_pubkey(key);
+
+	/* now parse the DER cert */
+	return NULL;
+}
+
+
+static sc_pkcs15_pubkey_t *
+coolkey_get_public_key_from_certificate(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj)
+{
+	sc_pkcs15_cert_info_t cert_info;
+	sc_pkcs15_cert_t *cert_out = NULL;
+	sc_pkcs15_pubkey_t *key = NULL;
+	int r;
+
+	cert_info.value.value = NULL;
+	r = coolkey_get_certificate(p15card->card, obj, &cert_info.value);
+	if (r < 0) {
+		goto fail;
+	}
+	r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
+	if (r < 0) {
+		goto fail;
+	}
+	key = cert_out->key;
+	cert_out->key = NULL; /* adopt the key from the cert */
+fail:
+	if (cert_out) {
+		sc_pkcs15_free_certificate(cert_out);
+	}
+	if (cert_info.value.value) {
+		free(cert_info.value.value);
+	}
+	return key;
+}
+
+static sc_pkcs15_pubkey_t *
+coolkey_get_public_key(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type)
+{
+	sc_pkcs15_pubkey_t *key;
+
+	key = coolkey_make_public_key(p15card->card, obj, key_type);
+	if (key) {
+		return key;
+	}
+	return coolkey_get_public_key_from_certificate(p15card, obj);
+}
+
+static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card)
+{
+	static const pindata pins[] = {
+		{ "1", NULL, "", 0x00,
+		  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
+		  32, 4, 32,
+		  SC_PKCS15_PIN_FLAG_INITIALIZED,
+		  -1, 0xFF,
+		  SC_PKCS15_CO_FLAG_PRIVATE },
+		{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+	};
+
+	/*
+	 * 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;
+	int count;
+
+
+	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+	memset(&serial, 0, sizeof(serial));
+
+	/* coolkey caches a nonce once it logs in, don't keep the pin around. The card will
+	 * stay logged in until it's been pulled from the reader, in which case you want to reauthenticate
+	 * anyway */
+	p15card->opts.use_pin_cache = 0;
+
+
+	/* get the token info from the card */
+	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_TOKEN_INFO, p15card->tokeninfo);
+	if (r < 0) {
+		/* put some defaults in if we didn't succeed */
+		p15card->tokeninfo->label = strdup("Coolkey");
+		p15card->tokeninfo->manufacturer_id = strdup("Unknown");
+		p15card->tokeninfo->serial_number = strdup("00000000");
+	}
+
+	/* set pins */
+	sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Coolkey 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 : p15card->tokeninfo->label;
+		sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Coolkey 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_COOLKEY_INIT_GET_OBJECTS, &count);
+	for (i = 0; i < count; i++) {
+		struct sc_cardctl_coolkey_object     coolkey_obj;
+		struct sc_pkcs15_object    obj_obj;
+		struct sc_pkcs15_cert_info cert_info;
+		struct sc_pkcs15_pubkey_info pubkey_info;
+		struct sc_pkcs15_prkey_info prkey_info;
+		sc_pkcs15_pubkey_t *key = NULL;
+		void *obj_info = NULL;
+		int obj_type = 0;
+		CK_KEY_TYPE key_type;
+		CK_OBJECT_CLASS obj_class;
+		size_t len;
+
+		r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT, &coolkey_obj);
+		if (r < 0)
+			SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+
+		memset(&obj_obj, 0, sizeof(obj_obj));
+		coolkey_get_attribute_bytes(card, &coolkey_obj, CKA_LABEL, (u8 *)obj_obj.label, &len, sizeof(obj_obj.label));
+		coolkey_get_flags(card, &coolkey_obj, &obj_obj.flags);
+		if (obj_obj.flags & SC_PKCS15_CO_FLAG_PRIVATE) {
+			sc_pkcs15_format_id(pins[0].id, &obj_obj.auth_id);
+		}
+
+		r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_CLASS, &obj_class);
+		if (r < 0) {
+			goto fail;
+		}
+		switch (obj_class) {
+		case CKO_PRIVATE_KEY:
+			r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type);
+			/* default to CKK_RSA */
+			if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
+				key_type = CKK_RSA;
+				r = SC_SUCCESS;
+			}
+			if (r < 0) {
+				goto fail;
+			}
+			/* set the info values */
+			obj_info = &prkey_info;
+			memset(&prkey_info, 0, sizeof(prkey_info));
+			coolkey_get_id(card, &coolkey_obj, &prkey_info.id);
+			prkey_info.path = coolkey_obj.path;
+			prkey_info.key_reference = coolkey_obj.id;
+			prkey_info.native = 1;
+			coolkey_get_usage(card, &coolkey_obj, &prkey_info.usage);
+			coolkey_get_access(card, &coolkey_obj, &prkey_info.access_flags);
+			key = coolkey_get_public_key(p15card, &coolkey_obj, key_type);
+			if (key_type == CKK_RSA) {
+				obj_type = SC_PKCS15_TYPE_PRKEY_RSA;
+				if (key) {
+					prkey_info.modulus_length =  key->u.rsa.modulus.len*8;
+				}
+			} else if (key_type == CKK_EC) {
+				obj_type = SC_PKCS15_TYPE_PUBKEY_EC;
+				if (key) {
+					prkey_info.field_length =  key->u.ec.params.field_length;
+				}
+			} else {
+				goto fail;
+			}
+			break;
+
+		case CKO_PUBLIC_KEY:
+			r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type);
+			/* default to CKK_RSA */
+			if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
+				key_type = CKK_RSA;
+				r = SC_SUCCESS;
+			}
+			if (r < 0) {
+				goto fail;
+			}
+			key = coolkey_get_public_key(p15card, &coolkey_obj, key_type);
+			if (key == NULL) {
+				goto fail;
+			}
+			/* set the info values */
+			obj_info = &pubkey_info;
+			memset(&pubkey_info, 0, sizeof(pubkey_info));
+			r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, key, &pubkey_info.direct.spki.value,
+																			&pubkey_info.direct.spki.len);
+			if (r < 0)
+				goto fail;
+			coolkey_get_id(card, &coolkey_obj, &pubkey_info.id);
+			pubkey_info.path = coolkey_obj.path;
+			pubkey_info.native        = 1;
+			pubkey_info.key_reference = coolkey_obj.id;
+			coolkey_get_usage(card, &coolkey_obj, &pubkey_info.usage);
+			coolkey_get_access(card, &coolkey_obj, &pubkey_info.access_flags);
+			if (key_type == CKK_RSA) {
+				obj_type = SC_PKCS15_TYPE_PUBKEY_RSA;
+				pubkey_info.modulus_length =  key->u.rsa.modulus.len*8;
+			} else if (key_type == CKK_EC) {
+				obj_type = SC_PKCS15_TYPE_PUBKEY_EC;
+				pubkey_info.field_length =  key->u.ec.params.field_length;
+			} else {
+				goto fail;
+			}
+			/* set the obj values */
+			obj_obj.emulated = key;
+			key = NULL;
+			break;
+
+		case CKO_CERTIFICATE:
+			obj_info = &cert_info;
+			memset(&cert_info, 0, sizeof(cert_info));
+			coolkey_get_id(card, &coolkey_obj, &cert_info.id);
+			cert_info.path = coolkey_obj.path;
+			obj_type = SC_PKCS15_TYPE_CERT_X509;
+
+			/* following will find the cached cert in cert_info */
+			r = coolkey_get_certificate(card, &coolkey_obj, &cert_info.value);
+			if (r < 0) {
+				goto fail;
+			}
+			break;
+
+
+		default:
+			/* no other recognized types which are stored 'on card' */
+			break;
+		}
+		if (obj_info == NULL) {
+			continue;
+		}
+
+		r = sc_pkcs15emu_object_add(p15card, obj_type, &obj_obj, obj_info);
+fail:
+		if (key) { sc_pkcs15_free_pubkey(key); }
+
+	}
+	r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS, &count);
+
+	SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card,
+				  sc_pkcs15emu_opt_t *opts)
+{
+	sc_card_t   *card = p15card->card;
+	sc_context_t    *ctx = card->ctx;
+
+	SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
+
+	if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
+		return sc_pkcs15emu_coolkey_init(p15card);
+	else {
+		int r = coolkey_detect_card(p15card);
+		if (r)
+			return SC_ERROR_WRONG_CARD;
+		return sc_pkcs15emu_coolkey_init(p15card);
+	}
+}
diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
index a89fc48..14f4d2f 100644
--- a/src/libopensc/pkcs15-syn.c
+++ b/src/libopensc/pkcs15-syn.c
@@ -57,6 +57,7 @@ struct sc_pkcs15_emulator_handler builtin_emulators[] = {
 	{ "gids",       sc_pkcs15emu_gids_init_ex   },
 	{ "iasecc",	sc_pkcs15emu_iasecc_init_ex   },
 	{ "jpki",	sc_pkcs15emu_jpki_init_ex },
+	{ "coolkey",    sc_pkcs15emu_coolkey_init_ex	},
 	{ NULL, NULL }
 };
 
diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h
index 8fa7912..5e3ae59 100644
--- a/src/libopensc/pkcs15-syn.h
+++ b/src/libopensc/pkcs15-syn.h
@@ -51,6 +51,7 @@ int sc_pkcs15emu_dnie_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_
 int sc_pkcs15emu_gids_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_opt_t *);
 int sc_pkcs15emu_iasecc_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_opt_t *);
 int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *,	struct sc_aid *, sc_pkcs15emu_opt_t *);
+int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card,	struct sc_aid *, sc_pkcs15emu_opt_t *opts);
 
 struct sc_pkcs15_emulator_handler {
 	const char *name;

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