#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
#include <openssl/evp.h>
#include <openssl/bio.h>
#include <openssl/pem.h>
#include <openssl/rand.h>
#include <openssl/rsa.h>
#endif
#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
#define COOLKEY_MAX_CHUNK_SIZE 240
#define ISO7816_CLASS 0x00
#define COOLKEY_CLASS 0xb0
#define ISO7816_INS_SELECT_FILE 0xa4
#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
#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
#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
#define COOLKEY_LIST_RESET 0x00
#define COOLKEY_LIST_NEXT 0x01
#define COOLKEY_DL_OBJECT_ID 0xffffffff
#define COOLKEY_COMBINED_OBJECT_ID 0x7a300000
#define COOLKEY_INVALID_KEY 0xff00
#define COOLKEY_KEY_CLASS 'k'
#define COOLKEY_NONCE_SIZE 8
typedef struct coolkey_life_cycle {
u8 life_cycle;
u8 pin_count;
u8 protocol_version_major;
u8 protocol_version_minor;
} coolkey_life_cycle_t;
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;
typedef struct coolkey_cuid {
u8 ic_fabricator[2];
u8 ic_type[2];
u8 ic_batch[2];
u8 ic_serial_number[4];
} coolkey_cuid_t;
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;
typedef struct coolkey_read_object_param {
u8 object_id[4];
u8 offset[4];
u8 length;
} coolkey_read_object_param_t;
typedef struct coolkey_write_object_param {
coolkey_read_object_param_t head;
u8 buf[COOLKEY_MAX_CHUNK_SIZE];
} coolkey_write_object_param_t;
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
typedef struct coolkey_decompressed_header {
u8 object_offset[2];
u8 object_count[2];
u8 token_name_length;
u8 token_name[255];
} coolkey_decompressed_header_t;
typedef struct coolkey_v0_object_header {
u8 record_type;
u8 object_id[4];
u8 attribute_data_len[2];
} coolkey_v0_object_header_t;
typedef struct coolkey_v0_attribute_header {
u8 attribute_attr_type[4];
u8 attribute_data_len[2];
} coolkey_v0_attribute_header_t;
typedef struct coolkey_combined_object_header {
u8 object_id[4];
u8 fixed_attributes_values[4];
u8 attribute_count[2];
} coolkey_combined_object_header_t;
typedef struct coolkey_object_header {
u8 record_type;
u8 object_id[4];
u8 fixed_attributes_values[4];
u8 attribute_count[2];
} coolkey_object_header_t;
#define COOLKEY_V0_OBJECT 0
#define COOLKEY_V1_OBJECT 1
typedef struct coolkey_attribute_header {
u8 attribute_attr_type[4];
u8 attribute_data_type;
} coolkey_attribute_header_t;
#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
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;
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:
*len = encoded_len ? 0: 1;
return SC_SUCCESS;
break;
case COOLKEY_ATTR_TYPE_INTEGER:
*len = 4;
return SC_SUCCESS;
default:
break;
}
return SC_ERROR_CORRUPTED_DATA;
}
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;
if (buf_len < sizeof(coolkey_v0_attribute_header_t)) {
return SC_ERROR_CORRUPTED_DATA;
}
*len = bebytes2ushort(attribute_head->attribute_data_len);
return SC_SUCCESS;
}
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;
}
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;
}
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;
}
return coolkey_v1_get_attribute_record_len(attr, buf_len);
}
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);
}
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;
}
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;
if (buf_len <= sizeof(coolkey_v0_object_header_t)) {
return 0;
}
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;
}
if (len > attribute_data_len) {
break;
}
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);
}
static int
coolkey_v0_get_attribute_data(const u8 *attr, size_t buf_len, sc_cardctl_coolkey_attribute_t *attr_out)
{
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;
}
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;
}
switch (attribute_head->attribute_data_type) {
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;
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;
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)
{
if (object_record_type == COOLKEY_V0_OBJECT) {
return coolkey_v0_get_attribute_data(attr, buf_len, attr_out);
}
if (object_record_type != COOLKEY_V1_OBJECT) {
return SC_ERROR_NO_CARD_SUPPORT;
}
return coolkey_v1_get_attribute_data(attr, buf_len, attr_out);
}
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;
}
static const u8 coolkey_static_cka_id[16] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
};
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 },
{ { 0, 0, 0, 1}, 0x00000380 },
{ { 0, 0, 0, 2}, 0x000c5f80 },
{ { 0, 0, 0, 3}, 0x00f3af80 },
{ { 0, 0, 0, 4}, 0x00f5ff80 },
{ { 0, 0, 0, 5}, 0x00000000 },
{ { 0, 0, 0, 6}, 0x00000000 },
{ { 0, 0, 0, 7}, 0x00000000 }
};
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;
}
mask = coolkey_static_cka_class[cka_class].boolean_mask;
bit = coolkey_get_fixed_boolean_bit(attr_type);
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;
}
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;
size_t token_name_length;
u8 nonce[COOLKEY_NONCE_SIZE];
int nonce_valid;
coolkey_cuid_t cuid;
sc_cardctl_coolkey_object_t *obj;
list_t objects_list;
unsigned short key_id;
int algorithm;
int operation;
} 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;
}
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;
priv = calloc(1, sizeof(coolkey_private_data_t));
if (!priv)
return NULL;
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;
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);
if (priv->token_name) {
free(priv->token_name);
}
free(priv);
return;
}
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) {
return SC_ERROR_INTERNAL;
}
if (sw2 >= coolkey_number_of_error_codes) {
return SC_ERROR_UNKNOWN;
}
return coolkey_error_codes[sw2].sc_error;
}
return sc_get_iso7816_driver()->ops->check_sw(card, sw1, sw2);
}
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 (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;
}
}
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;
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);
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);
}
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) {
return len;
}
if (len != 1) {
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) {
return len;
}
if (len != sizeof(status)) {
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;
}
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];
}
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;
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;
}
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;
}
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;
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;
}
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 (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;
}
len = MIN(count, priv->obj->length-idx);
memcpy(buf, &data[idx], len);
r = len;
priv->obj->data=data;
data = NULL;
done:
if (data)
free(data);
LOG_FUNC_RETURN(card->ctx, r);
}
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);
}
static int coolkey_get_init_and_get_count(list_t *list, int *countp)
{
*countp = list_size(list);
list_iterator_start(list);
return SC_SUCCESS;
}
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;
}
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) {
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);
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);
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;
}
if (obj_entry->data != NULL) {
free(new_obj_data);
return SC_ERROR_INTERNAL;
}
if (obj_entry->length != obj->length) {
free(new_obj_data);
return SC_ERROR_INTERNAL;
}
obj_entry->data = new_obj_data;
obj->data = new_obj_data;
return SC_SUCCESS;
}
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;
if (obj == NULL) {
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;
}
}
assert(sizeof(coolkey_object_header_t) >= sizeof(coolkey_v0_object_header_t));
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;
if ((object_record_type != COOLKEY_V1_OBJECT) && (object_record_type != COOLKEY_V0_OBJECT)) {
return SC_ERROR_CORRUPTED_DATA;
}
attr = coolkey_attribute_start(obj, object_record_type, buf_len);
if (attr == NULL) {
return SC_ERROR_CORRUPTED_DATA;
}
buf_len -= (attr-obj);
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);
if (buf_len < record_len || record_len < 4) {
return SC_ERROR_CORRUPTED_DATA;
}
if (attr_type == coolkey_get_attribute_type(attr, object_record_type, record_len)) {
return coolkey_get_attribute_data(attr, object_record_type, record_len, attribute);
}
buf_len -= record_len;
attr += record_len;
}
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;
}
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) {
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;
}
}
if (i == count) {
rv = current;
break;
}
}
list_iterator_stop(list);
if (tmp_pos != (unsigned int)-1) {
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;
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_log(card->ctx,
"datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\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;
if (priv->key_id > 0xff) {
r = SC_ERROR_NO_DEFAULT_KEY;
goto done;
}
params_len = sizeof(params.init) + crypt_in_len;
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_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;
}
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;
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;
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) {
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;
}
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);
if (decompressed_header->token_name_length +
offsetof(coolkey_decompressed_header_t,token_name) > decompressed_object_len) {
r = SC_ERROR_CORRUPTED_DATA;
goto done;
}
if (decompressed_header->token_name_length +
offsetof(coolkey_decompressed_header_t,token_name) > object_offset) {
r = SC_ERROR_CORRUPTED_DATA;
goto done;
}
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;
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;
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);
}
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;
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;
}
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;
r = coolkey_list_object(card, COOLKEY_LIST_RESET, &object_info);
while (r >= 0) {
unsigned long object_id;
unsigned short object_len;
if ((size_t)r < (sizeof(object_info)))
break;
object_id = bebytes2ulong(object_info.object_id);
object_len = bebytes2ulong(object_info.object_length);
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 {
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);
}
r = coolkey_list_object(card, COOLKEY_LIST_NEXT, &object_info);
}
if (r != SC_ERROR_FILE_END_REACHED) {
if (r >= 0) {
r = SC_ERROR_INVALID_CARD;
}
goto cleanup;
}
if (!combined_processed) {
global_platform_cplc_data_t cplc_data;
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;
return SC_SUCCESS;
cleanup:
if (priv) {
coolkey_free_private_data(priv);
}
return r;
}
static int coolkey_match_card(sc_card_t *card)
{
int r;
SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
card->ops->logout = NULL;
r = coolkey_select_applet(card);
if (r == SC_SUCCESS) {
sc_apdu_t apdu;
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;
flags = SC_ALGORITHM_RSA_RAW;
_sc_card_add_rsa_alg(card, 1024, flags, 0);
_sc_card_add_rsa_alg(card, 2048, flags, 0);
_sc_card_add_rsa_alg(card, 3072, flags, 0);
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;
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;
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:
default:
r = SC_ERROR_NOT_SUPPORTED;
break;
case SC_PIN_CMD_VERIFY:
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)
{
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));
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;
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();
}