/* * card-coolkey.c: Support for Coolkey * * Copyright (C) 2001, 2002 Juha Yrjölä * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert * Copyright (C) 2006, Identity Alliance, Thomas Harning * Copyright (C) 2007, EMC, Russell Larner * Copyright (C) 2016, Red Hat, Inc. * * Coolkey driver author: Robert Relyea * * 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 #include #include #include #include #ifdef _WIN32 #include #else #include #endif #include #ifdef ENABLE_OPENSSL /* openssl only needed for card administration */ #include #include #include #include #include #endif /* ENABLE_OPENSSL */ #include "internal.h" #include "asn1.h" #include "cardctl.h" #ifdef ENABLE_ZLIB #include "compression.h" #endif #include "iso7816.h" #include "gp.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 COOLKEY_CLASS 0xb0 /* ISO 71816 INS values used by COOLKEY */ #define ISO7816_INS_SELECT_FILE 0xa4 /* 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 0x72 #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; /* format of the coolkey_cuid, either constructed 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]; /* arbitrary 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 reserved:8; }; * cka_class is used to determine which booleans are valid. Any attributes in the full attribute list * takes precedence 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 (len + sizeof(coolkey_v0_attribute_header_t) > buf_len) { return SC_ERROR_CORRUPTED_DATA; } 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 changeable 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; } /* For SimCList autocopy, we need to know the size of the data elements */ size_t coolkey_list_meter(const void *el) { return sizeof(sc_cardctl_coolkey_object_t); } static void coolkey_free_private_data(coolkey_private_data_t *priv); 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)); if (!priv) return NULL; /* set other fields as appropriate */ priv->key_id = COOLKEY_INVALID_KEY; if (list_init(&priv->objects_list) != 0 || list_attributes_comparator(&priv->objects_list, coolkey_compare_id) != 0 || list_attributes_copy(&priv->objects_list, coolkey_list_meter, 1) != 0) { coolkey_free_private_data(priv); return NULL; } return priv; } static void coolkey_free_private_data(coolkey_private_data_t *priv) { list_t *l = &priv->objects_list; sc_cardctl_coolkey_object_t *o; /* Clean up the allocated memory in the items */ list_iterator_start(l); while (list_iterator_hasnext(l)) { o = (sc_cardctl_coolkey_object_t *)list_iterator_next(l); free(o->data); o->data = NULL; } list_iterator_stop(l); list_destroy(&priv->objects_list); 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) { if (list_append(list, object) < 0) return SC_ERROR_UNKNOWN; 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, "Reserved 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, "Reserved 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, "Reserved 0x9c0a" }, {SC_ERROR_SM_INVALID_CHECKSUM, "Signature invalid" }, {SC_ERROR_AUTH_METHOD_BLOCKED, "Identity blocked" }, {SC_ERROR_UNKNOWN, "Reserved 0x9c0d" }, {SC_ERROR_UNKNOWN, "Reserved 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_log(card->ctx, "sw1 = 0x%02x, sw2 = 0x%02x\n", sw1, sw2); if (sw1 == 0x90 && sw2 == 0x00) 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 similar 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_log(card->ctx, "%02x %02x %02x %"SC_FORMAT_LEN_SIZE_T"u : %"SC_FORMAT_LEN_SIZE_T"u %"SC_FORMAT_LEN_SIZE_T"u\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_log(card->ctx, "calling sc_transmit_apdu flags=%lx le=%"SC_FORMAT_LEN_SIZE_T"u, resplen=%"SC_FORMAT_LEN_SIZE_T"u, 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_log(card->ctx, "result r=%d apdu.resplen=%"SC_FORMAT_LEN_SIZE_T"u sw1=%02x sw2=%02x", r, apdu.resplen, apdu.sw1, apdu.sw2); if (r < 0) { sc_log(card->ctx, "Transmit failed"); goto err; } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r < 0) { sc_log(card->ctx, "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: LOG_FUNC_RETURN(card->ctx, 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 receive_len; int len; receive_len = sizeof(*life_cycle); receive_buf = (u8 *)life_cycle; len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, NULL, 0, &receive_buf, &receive_len, NULL, 0); if (len == sizeof(*life_cycle)) { return SC_SUCCESS; } receive_len = 1; receive_buf = &life_cycle->life_cycle; len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_LIFE_CYCLE, 0, 0, NULL, 0, &receive_buf, &receive_len, NULL, 0); if (len < 0) { /* Error from the trasmittion */ return len; } if (len != 1) { /* The returned data is invalid */ return SC_ERROR_INTERNAL; } receive_len = sizeof(status); receive_buf = (u8 *)&status; len = coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_STATUS, 0, 0, NULL, 0, &receive_buf, &receive_len, NULL, 0); if (len < 0) { /* Error from the trasmittion */ return len; } if (len != sizeof(status)) { /* The returned data is invalid */ return SC_ERROR_INTERNAL; } 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; } /* 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; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); 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; 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; } /* sanity 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: LOG_FUNC_RETURN(card->ctx, 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; size_t max_operation_len; /* set limit for the card's maximum send size and short write */ max_operation_len = MIN(COOLKEY_MAX_CHUNK_SIZE, (card->max_send_size - sizeof(coolkey_read_object_param_t) - nonce_size)); ulong2bebytes(¶ms.head.object_id[0], object_id); do { ulong2bebytes(¶ms.head.offset[0], offset); operation_len = MIN(left, max_operation_len); 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) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); } /* if we've already read the data, just return it */ if (priv->obj->data) { sc_log(card->ctx, "returning cached value idx=%u count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); len = MIN(count, priv->obj->length-idx); memcpy(buf, &priv->obj->data[idx], len); LOG_FUNC_RETURN(card->ctx, len); } sc_log(card->ctx, "clearing cache idx=%u count=%"SC_FORMAT_LEN_SIZE_T"u", 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); LOG_FUNC_RETURN(card->ctx, 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); LOG_FUNC_RETURN(card->ctx, 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; LOG_FUNC_CALLED(card->ctx); 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) { free(token_info->label); token_info->label = label; free(token_info->manufacturer_id); token_info->manufacturer_id = manufacturer_id; free(token_info->serial_number); 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); LOG_FUNC_CALLED(card->ctx); memcpy(serial->value, &priv->cuid, sizeof(priv->cuid)); serial->len = sizeof(priv->cuid); LOG_FUNC_RETURN(card->ctx, 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 = NULL; sc_cardctl_coolkey_object_t *obj_entry; coolkey_private_data_t * priv = COOLKEY_DATA(card); LOG_FUNC_CALLED(card->ctx); 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); if (r < 0) { LOG_FUNC_RETURN(card->ctx, r); } LOG_FUNC_RETURN(card->ctx, 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 */ } /* Make sure we will not go over the allocated limits in the other * objects if they somehow got different lengths in matching objects */ if (obj_entry->length != obj->length) { free(new_obj_data); return SC_ERROR_INTERNAL; /* shouldn't happen */ } obj_entry->data = new_obj_data; obj->data = new_obj_data; LOG_FUNC_RETURN(card->ctx, 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; int attribute_count,i; attribute->attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_STRING; attribute->attribute_length = 0; attribute->attribute_value = NULL; LOG_FUNC_CALLED(card->ctx); 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; if (obj == NULL) { return SC_ERROR_INTERNAL; } } /* 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_head = (coolkey_object_header_t *)obj; 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 || record_len < 4) { 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); } LOG_FUNC_RETURN(card->ctx, 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) { LOG_FUNC_CALLED(card->ctx); if (len > COOLKEY_MAX_CHUNK_SIZE) len = COOLKEY_MAX_CHUNK_SIZE; LOG_TEST_RET(card->ctx, coolkey_apdu_io(card, COOLKEY_CLASS, COOLKEY_INS_GET_RANDOM, 0, 0, NULL, 0, &rnd, &len, NULL, 0), "Could not get challenge"); LOG_FUNC_RETURN(card->ctx, (int) len); } 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_log(card->ctx, "flags=%08lx op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%"SC_FORMAT_LEN_SIZE_T"u\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); LOG_FUNC_RETURN(card->ctx, 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; u8 **crypt_out_p = NULL; size_t crypt_out_len_p = 0; coolkey_private_data_t *priv = COOLKEY_DATA(card); coolkey_compute_crypt_params_t params; u8 key_number; size_t params_len; u8 buf[MAX_COMPUTE_BUF + 2]; u8 *buf_out; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, max_out_len); if (priv->key_id > 0xff) { r = SC_ERROR_NO_DEFAULT_KEY; goto done; } key_number = priv->key_id; memset(¶ms, 0, sizeof(params)); params.init.mode = COOLKEY_CRYPT_MODE_RSA_NO_PAD; params.init.direction = COOLKEY_CRYPT_DIRECTION_ENCRYPT; /* for no pad, direction is irrelevant */ /* send the data to the card if necessary */ if (datalen > MAX_COMPUTE_BUF) { /* We need to write data to special object on the card as it does not safely fit APDU */ u8 len_buf[2]; params.init.location = COOLKEY_CRYPT_LOCATION_DL_OBJECT; params_len = sizeof(params.init); 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, 0); } else { /* The data fits in APDU. Copy it to the params object */ size_t buf_len; params.init.location = COOLKEY_CRYPT_LOCATION_APDU; params_len = sizeof(params.init) + datalen; buf_out = &buf[0]; crypt_out_p = &buf_out; buf_len = sizeof(buf); crypt_out_len_p = buf_len; ushort2bebytes(params.init.buf_len, datalen); memcpy(params.buf, data, datalen); } 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); if (out_length > sizeof buf - 2) { r = SC_ERROR_WRONG_LENGTH; goto done; } 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_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\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 parameter * 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) LOG_FUNC_RETURN(card->ctx, 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; *file_out = file; } 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; /* The object ID needs to be unique */ if (coolkey_find_object_by_id(&priv->objects_list, object_id) != NULL) { return SC_ERROR_INTERNAL; } 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 */ free(priv->token_name); 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; i++) { u8 *current_object = NULL; coolkey_combined_object_header_t *object_header = NULL; unsigned long object_id; int current_object_len; /* Can we read the object header at all? */ if ((object_offset + sizeof(coolkey_combined_object_header_t)) > decompressed_object_len) { r = SC_ERROR_CORRUPTED_DATA; goto done; } current_object = &decompressed_object[object_offset]; object_header = (coolkey_combined_object_header_t *)current_object; /* Parse object ID */ object_id = bebytes2ulong(object_header->object_id); /* 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 */ sc_log(card->ctx, "Add new object id=%ld", object_id); 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; } 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; } /* Select a coolkey read the coolkey objects out */ r = coolkey_select_applet(card); 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 */ r = coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info); while (r >= 0) { unsigned long object_id; unsigned long object_len; /* The card did not return what we expected: Lets try other objects */ if ((size_t)r < (sizeof(object_info))) break; /* TODO also look at the ACL... */ object_id = bebytes2ulong(object_info.object_id); object_len = bebytes2ulong(object_info.object_length); /* Avoid insanely large data */ if (object_len > MAX_FILE_SIZE) { r = SC_ERROR_CORRUPTED_DATA; goto cleanup; } /* 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; } else { sc_log(card->ctx, "Add new object id=%ld, len=%lu", object_id, object_len); r = coolkey_add_object(priv, object_id, NULL, object_len, 0); if (r != SC_SUCCESS) sc_log(card->ctx, "coolkey_add_object() returned %d", r); } /* Read next object: error is handled on the cycle condition and below after cycle */ r = coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info); } if (r != SC_ERROR_FILE_END_REACHED) { /* This means the card does not cooperate at all: bail out */ if (r >= 0) { r = SC_ERROR_INVALID_CARD; } 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; /* select the card manager, because a card with applet only will have already selected the coolkey applet */ r = gp_select_card_manager(card); if (r < 0) { goto cleanup; } r = gp_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; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); cleanup: if (priv) { coolkey_free_private_data(priv); } LOG_FUNC_RETURN(card->ctx, 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); if (r == SC_SUCCESS) { sc_apdu_t apdu; /* The GET STATUS INS with P1 = 1 returns invalid instruction (0x6D00) * on Coolkey applet (reserved for GetMemory function), * while incorrect P1 (0x9C10) on Muscle applets */ sc_format_apdu(card, &apdu, SC_APDU_CASE_1, COOLKEY_INS_GET_STATUS, 0x01, 0x00); apdu.cla = COOLKEY_CLASS; apdu.le = 0x00; apdu.resplen = 0; apdu.resp = NULL; r = sc_transmit_apdu(card, &apdu); if (r == SC_SUCCESS && apdu.sw1 == 0x6d && apdu.sw2 == 0x00) { return 1; } return 0; } return 0; } 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) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } 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); /* mandatory */ _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; } LOG_FUNC_RETURN(card->ctx, 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 int coolkey_card_reader_lock_obtained(sc_card_t *card, int was_reset) { int r = SC_SUCCESS; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (was_reset > 0) { r = coolkey_select_applet(card); } LOG_FUNC_RETURN(card->ctx, r); } 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; coolkey_ops.card_reader_lock_obtained = coolkey_card_reader_lock_obtained; return &coolkey_drv; } struct sc_card_driver * sc_get_coolkey_driver(void) { return sc_get_driver(); }