[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(¶ms.object_id[0], object_id);
+
+ out_ptr = out_buf;
+ left = out_len;
+ do {
+ ulong2bebytes(¶ms.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 *)¶ms, 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(¶ms.head.object_id[0], object_id);
+
+ do {
+ ulong2bebytes(¶ms.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 *)¶ms, 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 *)¶ms, 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 *)¶ms, 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