/* * card-cac.c: Support for CAC from NIST SP800-73 * card-default.c: Support for cards with no driver * * Copyright (C) 2001, 2002 Juha Yrjölä * 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 - 2018, Red Hat, Inc. * * CAC driver author: Robert Relyea * Further work: Jakub Jelen * * 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 #ifdef ENABLE_OPENSSL #include #endif /* ENABLE_OPENSSL */ #include "internal.h" #include "simpletlv.h" #include "cardctl.h" #ifdef ENABLE_ZLIB #include "compression.h" #endif #include "iso7816.h" #include "card-cac-common.h" #include "pkcs15.h" /* * CAC hardware and APDU constants */ #define CAC_MAX_CHUNK_SIZE 240 #define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */ #define CAC_INS_READ_FILE 0x52 /* read a TL or V file */ #define CAC_INS_GET_ACR 0x4c #define CAC_INS_GET_PROPERTIES 0x56 #define CAC_P1_STEP 0x80 #define CAC_P1_FINAL 0x00 #define CAC_FILE_TAG 1 #define CAC_FILE_VALUE 2 /* TAGS in a TL file */ #define CAC_TAG_CERTIFICATE 0x70 #define CAC_TAG_CERTINFO 0x71 #define CAC_TAG_MSCUID 0x72 #define CAC_TAG_CUID 0xF0 #define CAC_TAG_CC_VERSION_NUMBER 0xF1 #define CAC_TAG_GRAMMAR_VERION_NUMBER 0xF2 #define CAC_TAG_CARDURL 0xF3 #define CAC_TAG_PKCS15 0xF4 #define CAC_TAG_ACCESS_CONTROL 0xF6 #define CAC_TAG_DATA_MODEL 0xF5 #define CAC_TAG_CARD_APDU 0xF7 #define CAC_TAG_REDIRECTION 0xFA #define CAC_TAG_CAPABILITY_TUPLES 0xFB #define CAC_TAG_STATUS_TUPLES 0xFC #define CAC_TAG_NEXT_CCC 0xFD #define CAC_TAG_ERROR_CODES 0xFE #define CAC_TAG_APPLET_FAMILY 0x01 #define CAC_TAG_NUMBER_APPLETS 0x94 #define CAC_TAG_APPLET_ENTRY 0x93 #define CAC_TAG_APPLET_AID 0x92 #define CAC_TAG_APPLET_INFORMATION 0x01 #define CAC_TAG_NUMBER_OF_OBJECTS 0x40 #define CAC_TAG_TV_BUFFER 0x50 #define CAC_TAG_PKI_OBJECT 0x51 #define CAC_TAG_OBJECT_ID 0x41 #define CAC_TAG_BUFFER_PROPERTIES 0x42 #define CAC_TAG_PKI_PROPERTIES 0x43 #define CAC_APP_TYPE_GENERAL 0x01 #define CAC_APP_TYPE_SKI 0x02 #define CAC_APP_TYPE_PKI 0x04 #define CAC_ACR_ACR 0x00 #define CAC_ACR_APPLET_OBJECT 0x10 #define CAC_ACR_AMP 0x20 #define CAC_ACR_SERVICE 0x21 #define CAC_MAX_CCC_DEPTH 16 /* hardware data structures (returned in the CCC) */ /* part of the card_url */ typedef struct cac_access_profile { u8 GCACR_listID; u8 GCACR_readTagListACRID; u8 GCACR_updatevalueACRID; u8 GCACR_readvalueACRID; u8 GCACR_createACRID; u8 GCACR_deleteACRID; u8 CryptoACR_listID; u8 CryptoACR_getChallengeACRID; u8 CryptoACR_internalAuthenicateACRID; u8 CryptoACR_pkiComputeACRID; u8 CryptoACR_readTagListACRID; u8 CryptoACR_updatevalueACRID; u8 CryptoACR_readvalueACRID; u8 CryptoACR_createACRID; u8 CryptoACR_deleteACRID; } cac_access_profile_t; /* part of the card url */ typedef struct cac_access_key_info { u8 keyFileID[2]; u8 keynumber; } cac_access_key_info_t; typedef struct cac_card_url { u8 rid[5]; u8 cardApplicationType; u8 objectID[2]; u8 applicationID[2]; cac_access_profile_t accessProfile; u8 pinID; /* not used for VM cards */ cac_access_key_info_t accessKeyInfo; /* not used for VM cards */ u8 keyCryptoAlgorithm; /* not used for VM cards */ } cac_card_url_t; #define CAC_MAX_OBJECTS 16 typedef struct { /* OID has two bytes */ unsigned char oid[2]; /* Format is NOT SimpleTLV? */ unsigned char simpletlv; /* Is certificate object and private key is initialized */ unsigned char privatekey; } cac_properties_object_t; typedef struct { unsigned int num_objects; cac_properties_object_t objects[CAC_MAX_OBJECTS]; } cac_properties_t; /* * Flags for Current Selected Object Type * CAC files are TLV files, with TL and V separated. For generic * containers we reintegrate the TL anv V portions into a single * file to read. Certs are also TLV files, but pkcs15 wants the * actual certificate. At select time we know the patch which tells * us what time of files we want to read. We remember that type * so that read_binary can do the appropriate processing. */ #define CAC_OBJECT_TYPE_CERT 1 #define CAC_OBJECT_TYPE_TLV_FILE 4 #define CAC_OBJECT_TYPE_GENERIC 5 /* * Set up the normal CAC paths */ #define CAC_2_RID "\xA0\x00\x00\x01\x16" static const sc_path_t cac_ACA_Path = { "", 0, 0,0,SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x10\x00") } }; static const sc_path_t cac_CCC_Path = { "", 0, 0,0,SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_2_RID "\xDB\x00") } }; /* * CAC general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0. * doubles as a source for CAC-2 labels. */ static const cac_object_t cac_objects[] = { { "Person Instance", 0x200, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x00") }}}, { "Personnel", 0x201, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x01") }}}, { "Benefits", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x02") }}}, { "Other Benefits", 0x203, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\x03") }}}, { "PKI Credential", 0x2FD, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\xFD") }}}, { "PKI Certificate", 0x2FE, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME, { CAC_TO_AID(CAC_1_RID "\x02\xFE") }}}, }; static const int cac_object_count = sizeof(cac_objects)/sizeof(cac_objects[0]); /* * use the object id to find our object info on the object in our CAC-1 list */ static const cac_object_t *cac_find_obj_by_id(unsigned short object_id) { int i; for (i = 0; i < cac_object_count; i++) { if (cac_objects[i].fd == object_id) { return &cac_objects[i]; } } return NULL; } /* * Lookup the path in the pki list to see if it is a cert path */ static int cac_is_cert(cac_private_data_t * priv, const sc_path_t *in_path) { cac_object_t test_obj; test_obj.path = *in_path; test_obj.path.index = 0; test_obj.path.count = 0; return (list_contains(&priv->pki_list, &test_obj) != 0); } /* * Send a command and receive data. * * A caller may provide a buffer, and length to read. If not provided, * an internal 4096 byte buffer is used, and a copy is returned to the * caller. that need to be freed by the caller. * * modelled after a similar function in card-piv.c */ static int cac_apdu_io(sc_card_t *card, int ins, int p1, int p2, const u8 * sendbuf, size_t sendbuflen, u8 ** recvbuf, size_t * recvbuflen) { int r; sc_apdu_t apdu; u8 rbufinitbuf[CAC_MAX_SIZE]; u8 *rbuf; size_t rbuflen; unsigned int apdu_case = SC_APDU_CASE_1; 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 (recvbuf) { if (sendbuf) apdu_case = SC_APDU_CASE_4_SHORT; else apdu_case = SC_APDU_CASE_2_SHORT; } else if (sendbuf) apdu_case = SC_APDU_CASE_3_SHORT; sc_format_apdu(card, &apdu, apdu_case, ins, p1, p2); apdu.lc = sendbuflen; apdu.datalen = sendbuflen; apdu.data = sendbuf; if (recvbuf) { apdu.resp = rbuf; apdu.le = (rbuflen > 255) ? 255 : rbuflen; apdu.resplen = rbuflen; } else { apdu.resp = rbuf; apdu.le = 0; apdu.resplen = 0; } sc_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, "Card returned error "); goto err; } if (recvbuflen) { if (recvbuf && *recvbuf == NULL) { *recvbuf = malloc(apdu.resplen); if (*recvbuf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } memcpy(*recvbuf, rbuf, apdu.resplen); } *recvbuflen = apdu.resplen; r = *recvbuflen; } err: LOG_FUNC_RETURN(card->ctx, r); } /* * Get ACR of currently ACA applet identified by the acr_type * 5.3.3.5 Get ACR APDU */ static int cac_get_acr(sc_card_t *card, int acr_type, u8 **out_buf, size_t *out_len) { u8 *out = NULL; /* XXX assuming it will not be longer than 255 B */ size_t len = 256; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* for simplicity we support only ACR without arguments now */ if (acr_type != 0x00 && acr_type != 0x10 && acr_type != 0x20 && acr_type != 0x21) { return SC_ERROR_INVALID_ARGUMENTS; } r = cac_apdu_io(card, CAC_INS_GET_ACR, acr_type, 0, NULL, 0, &out, &len); if (len == 0) { r = SC_ERROR_FILE_NOT_FOUND; } if (r < 0) goto fail; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "got %"SC_FORMAT_LEN_SIZE_T"u bytes out=%p", len, out); *out_len = len; *out_buf = out; return SC_SUCCESS; fail: if (out) free(out); *out_buf = NULL; *out_len = 0; return r; } /* * Read a CAC TLV file. Parameters specify if the TLV file is TL (Tag/Length) file or a V (value) file */ #define HIGH_BYTE_OF_SHORT(x) (((x)>> 8) & 0xff) #define LOW_BYTE_OF_SHORT(x) ((x) & 0xff) static int cac_read_file(sc_card_t *card, int file_type, u8 **out_buf, size_t *out_len) { u8 params[2]; u8 count[2]; u8 *out = NULL; u8 *out_ptr; size_t offset = 0; size_t size = 0; size_t left = 0; size_t len; int r; params[0] = file_type; params[1] = 2; /* get the size */ len = sizeof(count); out_ptr = count; r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, ¶ms[0], sizeof(params), &out_ptr, &len); if (len == 0) { r = SC_ERROR_FILE_NOT_FOUND; } if (r < 0) goto fail; left = size = lebytes2ushort(count); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "got %"SC_FORMAT_LEN_SIZE_T"u bytes out_ptr=%p count&=%p count[0]=0x%02x count[1]=0x%02x, len=0x%04"SC_FORMAT_LEN_SIZE_T"x (%"SC_FORMAT_LEN_SIZE_T"u)", len, out_ptr, &count, count[0], count[1], size, size); out = out_ptr = malloc(size); if (out == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto fail; } for (offset += 2; left > 0; offset += len, left -= len, out_ptr += len) { len = MIN(left, CAC_MAX_CHUNK_SIZE); params[1] = len; r = cac_apdu_io(card, CAC_INS_READ_FILE, HIGH_BYTE_OF_SHORT(offset), LOW_BYTE_OF_SHORT(offset), ¶ms[0], sizeof(params), &out_ptr, &len); /* if there is no data, assume there is no file */ if (len == 0) { r = SC_ERROR_FILE_NOT_FOUND; } if (r < 0) { goto fail; } } *out_len = size; *out_buf = out; return SC_SUCCESS; fail: if (out) free(out); *out_len = 0; return r; } /* * Callers of this may be expecting a certificate, * select file will have saved the object type for us * as well as set that we want the cert from the object. */ static int cac_read_binary(sc_card_t *card, unsigned int idx, unsigned char *buf, size_t count, unsigned long flags) { cac_private_data_t * priv = CAC_DATA(card); int r = 0; u8 *tl = NULL, *val = NULL; const u8 *tl_ptr, *val_ptr, *tl_start; u8 *tlv_ptr; const u8 *cert_ptr; size_t tl_len, val_len, tlv_len; size_t len, tl_head_len, cert_len; u8 cert_type, tag; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* if we didn't return it all last time, return the remainder */ if (priv->cached) { sc_log(card->ctx, "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); if (idx > priv->cache_buf_len) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_END_REACHED); } len = MIN(count, priv->cache_buf_len-idx); memcpy(buf, &priv->cache_buf[idx], len); LOG_FUNC_RETURN(card->ctx, len); } sc_log(card->ctx, "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u", idx, count); if (priv->cache_buf) { free(priv->cache_buf); priv->cache_buf = NULL; priv->cache_buf_len = 0; } if (priv->object_type <= 0) LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len); if (r < 0) { goto done; } r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len); if (r < 0) goto done; switch (priv->object_type) { case CAC_OBJECT_TYPE_TLV_FILE: tlv_len = tl_len + val_len; priv->cache_buf = malloc(tlv_len); if (priv->cache_buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } priv->cache_buf_len = tlv_len; for (tl_ptr = tl, val_ptr=val, tlv_ptr = priv->cache_buf; tl_len >= 2 && tlv_len > 0; val_len -= len, tlv_len -= len, val_ptr += len, tlv_ptr += len) { /* get the tag and the length */ tl_start = tl_ptr; r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len); if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) break; tl_head_len = (tl_ptr - tl_start); sc_simpletlv_put_tag(tag, len, tlv_ptr, tlv_len, &tlv_ptr); tlv_len -= tl_head_len; tl_len -= tl_head_len; /* don't crash on bad data */ if (val_len < len) { sc_log(card->ctx, "Received too long value %"SC_FORMAT_LEN_SIZE_T"u, " "while only %"SC_FORMAT_LEN_SIZE_T"u left. Truncating", len, val_len); len = val_len; } /* if we run out of return space, truncate */ if (tlv_len < len) { len = tlv_len; } memcpy(tlv_ptr, val_ptr, len); } break; case CAC_OBJECT_TYPE_CERT: /* read file */ sc_log(card->ctx, " obj= cert_file, val_len=%"SC_FORMAT_LEN_SIZE_T"u (0x%04"SC_FORMAT_LEN_SIZE_T"x)", val_len, val_len); cert_len = 0; cert_ptr = NULL; cert_type = 0; for (tl_ptr = tl, val_ptr = val; tl_len >= 2; val_len -= len, val_ptr += len, tl_len -= tl_head_len) { tl_start = tl_ptr; r = sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len); if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) break; tl_head_len = tl_ptr - tl_start; /* incomplete value */ if (val_len < len) { sc_log(card->ctx, "Read incomplete value %"SC_FORMAT_LEN_SIZE_T"u, " "while only %"SC_FORMAT_LEN_SIZE_T"u left", len, val_len); break; } if (tag == CAC_TAG_CERTIFICATE) { cert_len = len; cert_ptr = val_ptr; } if (tag == CAC_TAG_CERTINFO) { if ((len >= 1) && (val_len >=1)) { cert_type = *val_ptr; } } if (tag == CAC_TAG_MSCUID) { sc_log_hex(card->ctx, "MSCUID", val_ptr, len); } } /* if the info byte is 1, then the cert is compressed, decompress it */ if ((cert_type & 0x3) == 1) { #ifdef ENABLE_ZLIB r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len, cert_ptr, cert_len, COMPRESSION_AUTO); #else sc_log(card->ctx, "CAC compression not supported, no zlib"); r = SC_ERROR_NOT_SUPPORTED; #endif if (r) goto done; } else if (cert_len > 0) { priv->cache_buf = malloc(cert_len); if (priv->cache_buf == NULL) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } priv->cache_buf_len = cert_len; memcpy(priv->cache_buf, cert_ptr, cert_len); } else { sc_log(card->ctx, "Can't read zero-length certificate"); goto done; } break; case CAC_OBJECT_TYPE_GENERIC: /* TODO * We have some two buffers in unknown encoding that we * need to present in PKCS#15 layer. */ default: /* Unknown object type */ sc_log(card->ctx, "Unknown object type: %x", priv->object_type); r = SC_ERROR_INTERNAL; goto done; } /* OK we've read the data, now copy the required portion out to the callers buffer */ priv->cached = 1; len = MIN(count, priv->cache_buf_len-idx); memcpy(buf, &priv->cache_buf[idx], len); r = len; done: if (tl) free(tl); if (val) free(val); LOG_FUNC_RETURN(card->ctx, r); } /* initialize getting a list and return the number of elements in the list */ static int cac_get_init_and_get_count(list_t *list, cac_object_t **entry, int *countp) { *countp = list_size(list); list_iterator_start(list); *entry = list_iterator_next(list); return SC_SUCCESS; } /* finalize the list iterator */ static int cac_final_iterator(list_t *list) { list_iterator_stop(list); return SC_SUCCESS; } /* fill in the obj_info for the current object on the list and advance to the next object */ static int cac_fill_object_info(list_t *list, cac_object_t **entry, sc_pkcs15_data_info_t *obj_info) { memset(obj_info, 0, sizeof(sc_pkcs15_data_info_t)); if (*entry == NULL) { return SC_ERROR_FILE_END_REACHED; } obj_info->path = (*entry)->path; obj_info->path.count = CAC_MAX_SIZE-1; /* read something from the object */ obj_info->id.value[0] = ((*entry)->fd >> 8) & 0xff; obj_info->id.value[1] = (*entry)->fd & 0xff; obj_info->id.len = 2; strncpy(obj_info->app_label, (*entry)->name, SC_PKCS15_MAX_LABEL_SIZE-1); *entry = list_iterator_next(list); return SC_SUCCESS; } static int cac_get_serial_nr_from_CUID(sc_card_t* card, sc_serial_number_t* serial) { cac_private_data_t * priv = CAC_DATA(card); LOG_FUNC_CALLED(card->ctx); if (card->serialnr.len) { *serial = card->serialnr; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } if (priv->cac_id_len) { serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR); memcpy(serial->value, priv->cac_id, serial->len); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_FILE_NOT_FOUND); } static int cac_get_ACA_path(sc_card_t *card, sc_path_t *path) { cac_private_data_t * priv = CAC_DATA(card); LOG_FUNC_CALLED(card->ctx); if (priv->aca_path) { *path = *priv->aca_path; } LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int cac_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) { cac_private_data_t * priv = CAC_DATA(card); LOG_FUNC_CALLED(card->ctx); sc_log(card->ctx, "cmd=%ld ptr=%p", cmd, ptr); if (priv == NULL) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INTERNAL); } switch(cmd) { case SC_CARDCTL_CAC_GET_ACA_PATH: return cac_get_ACA_path(card, (sc_path_t *) ptr); case SC_CARDCTL_GET_SERIALNR: return cac_get_serial_nr_from_CUID(card, (sc_serial_number_t *) ptr); case SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS: return cac_get_init_and_get_count(&priv->general_list, &priv->general_current, (int *)ptr); case SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS: return cac_get_init_and_get_count(&priv->pki_list, &priv->pki_current, (int *)ptr); case SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT: return cac_fill_object_info(&priv->general_list, &priv->general_current, (sc_pkcs15_data_info_t *)ptr); case SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT: return cac_fill_object_info(&priv->pki_list, &priv->pki_current, (sc_pkcs15_data_info_t *)ptr); case SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS: return cac_final_iterator(&priv->general_list); case SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS: return cac_final_iterator(&priv->pki_list); } LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_SUPPORTED); } static int cac_get_challenge(sc_card_t *card, u8 *rnd, size_t len) { /* CAC requires 8 byte response */ u8 rbuf[8]; u8 *rbufp = &rbuf[0]; size_t out_len = sizeof rbuf; int r; LOG_FUNC_CALLED(card->ctx); r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &out_len); LOG_TEST_RET(card->ctx, r, "Could not get challenge"); if (len < out_len) { out_len = len; } memcpy(rnd, rbuf, out_len); LOG_FUNC_RETURN(card->ctx, (int) out_len); } static int cac_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { int r = SC_SUCCESS; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_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) { r = SC_ERROR_NO_CARD_SUPPORT; } SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int cac_restore_security_env(sc_card_t *card, int se_num) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int cac_rsa_op(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { int r; u8 *outp, *rbuf; size_t rbuflen, outplen; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); sc_log(card->ctx, "datalen=%"SC_FORMAT_LEN_SIZE_T"u outlen=%"SC_FORMAT_LEN_SIZE_T"u\n", datalen, outlen); outp = out; outplen = outlen; /* Not strictly necessary. This code requires the caller to have selected the correct PKI container * and authenticated to that container with the verifyPin command... All of this under the reader lock. * The PKCS #15 higher level driver code does all this correctly (it's the same for all cards, just * different sets of APDU's that need to be called), so this call is really a little bit of paranoia */ r = sc_lock(card); if (r != SC_SUCCESS) LOG_FUNC_RETURN(card->ctx, r); rbuf = NULL; rbuflen = 0; for (; datalen > CAC_MAX_CHUNK_SIZE; data += CAC_MAX_CHUNK_SIZE, datalen -= CAC_MAX_CHUNK_SIZE) { r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_STEP, 0, data, CAC_MAX_CHUNK_SIZE, &rbuf, &rbuflen); if (r < 0) { break; } if (rbuflen != 0) { int n = MIN(rbuflen, outplen); memcpy(outp,rbuf, n); outp += n; outplen -= n; } free(rbuf); rbuf = NULL; rbuflen = 0; } if (r < 0) { goto err; } rbuf = NULL; rbuflen = 0; r = cac_apdu_io(card, CAC_INS_SIGN_DECRYPT, CAC_P1_FINAL, 0, data, datalen, &rbuf, &rbuflen); if (r < 0) { goto err; } if (rbuflen != 0) { int n = MIN(rbuflen, outplen); memcpy(outp,rbuf, n); /*outp += n; unused */ outplen -= n; } free(rbuf); rbuf = NULL; r = outlen-outplen; err: sc_unlock(card); if (r < 0) { sc_mem_clear(out, outlen); } if (rbuf) { free(rbuf); } LOG_FUNC_RETURN(card->ctx, r); } static int cac_compute_signature(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen)); } static int cac_decipher(sc_card_t *card, const u8 * data, size_t datalen, u8 * out, size_t outlen) { SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, cac_rsa_op(card, data, datalen, out, outlen)); } static int cac_parse_properties_object(sc_card_t *card, u8 type, const u8 *data, size_t data_len, cac_properties_object_t *object) { size_t len; const u8 *val, *val_end; u8 tag; int parsed = 0; if (data_len < 11) return -1; /* Initialize: non-PKI applet */ object->privatekey = 0; val = data; val_end = data + data_len; for (; val < val_end; val += len) { /* get the tag and the length */ if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) break; switch (tag) { case CAC_TAG_OBJECT_ID: if (len != 2) { sc_log(card->ctx, "TAG: Object ID: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Object ID = 0x%02x 0x%02x", val[0], val[1]); memcpy(&object->oid, val, 2); parsed++; break; case CAC_TAG_BUFFER_PROPERTIES: if (len != 5) { sc_log(card->ctx, "TAG: Buffer Properties: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } /* First byte is "Type of Tag Supported" */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Buffer Properties: Type of Tag Supported = 0x%02x", val[0]); object->simpletlv = val[0]; parsed++; break; case CAC_TAG_PKI_PROPERTIES: /* 4th byte is "Private Key Initialized" */ if (len != 4) { sc_log(card->ctx, "TAG: PKI Properties: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } if (type != CAC_TAG_PKI_OBJECT) { sc_log(card->ctx, "TAG: PKI Properties outside of PKI Object"); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: PKI Properties: Private Key Initialized = 0x%02x", val[2]); object->privatekey = val[2]; parsed++; break; default: /* ignore tags we don't understand */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Unknown (0x%02x)",tag ); break; } } if (parsed < 2) return SC_ERROR_INVALID_DATA; return SC_SUCCESS; } static int cac_get_properties(sc_card_t *card, cac_properties_t *prop) { u8 *rbuf = NULL; size_t rbuflen = 0, len; const u8 *val, *val_end; u8 tag; size_t i = 0; int r; prop->num_objects = 0; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = cac_apdu_io(card, CAC_INS_GET_PROPERTIES, 0x01, 0x00, NULL, 0, &rbuf, &rbuflen); if (r < 0) return r; val = rbuf; val_end = val + rbuflen; for (; val < val_end; val += len) { /* get the tag and the length */ if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) break; switch (tag) { case CAC_TAG_APPLET_INFORMATION: if (len != 5) { sc_log(card->ctx, "TAG: Applet Information: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Applet Information: Family: 0x%0x", val[0]); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, " Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x", val[1], val[2], val[3], val[4]); break; case CAC_TAG_NUMBER_OF_OBJECTS: if (len != 1) { sc_log(card->ctx, "TAG: Num objects: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Num objects = %hhd", *val); /* make sure we do not overrun buffer */ prop->num_objects = MIN(val[0], CAC_MAX_OBJECTS); break; case CAC_TAG_TV_BUFFER: if (len != 17) { sc_log(card->ctx, "TAG: TV Object: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: TV Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i); if (i >= CAC_MAX_OBJECTS) { free(rbuf); return SC_SUCCESS; } if (cac_parse_properties_object(card, tag, val, len, &prop->objects[i]) == SC_SUCCESS) i++; break; case CAC_TAG_PKI_OBJECT: if (len != 17) { sc_log(card->ctx, "TAG: PKI Object: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: PKI Object nr. %"SC_FORMAT_LEN_SIZE_T"u", i); if (i >= CAC_MAX_OBJECTS) { free(rbuf); return SC_SUCCESS; } if (cac_parse_properties_object(card, tag, val, len, &prop->objects[i]) == SC_SUCCESS) i++; break; default: /* ignore tags we don't understand */ sc_log(card->ctx, "TAG: Unknown (0x%02x), len=%" SC_FORMAT_LEN_SIZE_T"u", tag, len); break; } } free(rbuf); /* sanity */ if (i != prop->num_objects) sc_log(card->ctx, "The announced number of objects (%u) " "did not match reality (%"SC_FORMAT_LEN_SIZE_T"u)", prop->num_objects, i); prop->num_objects = i; return SC_SUCCESS; } /* * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS * if it doesn't like anything about the select, so we always 'request' FCI for CAC1 * * The rest is just copied from iso7816_select_file */ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { struct sc_context *ctx; struct sc_apdu apdu; unsigned char buf[SC_MAX_APDU_BUFFER_SIZE]; unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf; int r, pathlen, pathtype; struct sc_file *file = NULL; cac_private_data_t * priv = CAC_DATA(card); assert(card != NULL && in_path != NULL); ctx = card->ctx; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); memcpy(path, in_path->value, in_path->len); pathlen = in_path->len; pathtype = in_path->type; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "path=%s, path->value=%s path->type=%d (%x)", sc_print_path(in_path), sc_dump_hex(in_path->value, in_path->len), in_path->type, in_path->type); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n", file_out, in_path->index, in_path->count); /* Sigh, iso7816_select_file expects paths to keys to have specific * formats. There is no override. We have to add some bytes to the * path to make it happy. * We only need to do this for private keys. */ if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) { path += 2; pathlen -= 2; } /* CAC has multiple different type of objects that aren't PKCS #15. When we read * them we need convert them to something PKCS #15 would understand. Find the object * and object type here: */ if (priv) { /* don't record anything if we haven't been initialized yet */ priv->object_type = CAC_OBJECT_TYPE_GENERIC; if (cac_is_cert(priv, in_path)) { priv->object_type = CAC_OBJECT_TYPE_CERT; } /* forget any old cached values */ if (priv->cache_buf) { free(priv->cache_buf); priv->cache_buf = NULL; } priv->cache_buf_len = 0; priv->cached = 0; } if (in_path->aid.len) { if (!pathlen) { memcpy(path, in_path->aid.value, in_path->aid.len); pathlen = in_path->aid.len; pathtype = SC_PATH_TYPE_DF_NAME; } else { /* First, select the application */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" ); sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0); apdu.data = in_path->aid.value; apdu.datalen = in_path->aid.len; apdu.lc = in_path->aid.len; r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) LOG_FUNC_RETURN(ctx, r); } } sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); switch (pathtype) { /* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select. * Unfortunately we'd also need to update the caching code as well. For now just * use FILE_ID and change p1 here */ case SC_PATH_TYPE_FILE_ID: apdu.p1 = 2; if (pathlen != 2) return SC_ERROR_INVALID_ARGUMENTS; break; case SC_PATH_TYPE_DF_NAME: apdu.p1 = 4; break; default: LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS); } apdu.lc = pathlen; apdu.data = path; apdu.datalen = pathlen; apdu.resp = buf; apdu.resplen = sizeof(buf); apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256; if (file_out != NULL) { apdu.p2 = 0; /* first record, return FCI */ } else { apdu.p2 = 0x0C; } r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(ctx, r, "APDU transmit failed"); if (file_out == NULL) { /* For some cards 'SELECT' can be only with request to return FCI/FCP. */ r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) { apdu.p2 = 0x00; apdu.resplen = sizeof(buf); if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS) r = sc_check_sw(card, apdu.sw1, apdu.sw2); } if (apdu.sw1 == 0x61) LOG_FUNC_RETURN(ctx, SC_SUCCESS); LOG_FUNC_RETURN(ctx, r); } r = sc_check_sw(card, apdu.sw1, apdu.sw2); if (r) LOG_FUNC_RETURN(ctx, r); /* This needs to come after the applet selection */ if (priv && in_path->len >= 2) { /* get applet properties to know if we can treat the * buffer as SimpleLTV and if we have PKI applet. * * Do this only if we select applets for reading * (not during driver initialization) */ cac_properties_t prop; size_t i = -1; r = cac_get_properties(card, &prop); if (r == SC_SUCCESS) { for (i = 0; i < prop.num_objects; i++) { sc_log(card->ctx, "Searching for our OID: 0x%02x 0x%02x = 0x%02x 0x%02x", prop.objects[i].oid[0], prop.objects[i].oid[1], in_path->value[0], in_path->value[1]); if (memcmp(prop.objects[i].oid, in_path->value, 2) == 0) break; } } if (i < prop.num_objects) { if (prop.objects[i].privatekey) priv->object_type = CAC_OBJECT_TYPE_CERT; else if (prop.objects[i].simpletlv == 0) priv->object_type = CAC_OBJECT_TYPE_TLV_FILE; } } /* CAC cards never return FCI, fake one */ file = sc_file_new(); if (file == NULL) LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY); file->path = *in_path; file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */ *file_out = file; LOG_FUNC_RETURN(ctx, SC_SUCCESS); } static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out) { return cac_select_file_by_type(card, in_path, file_out); } static int cac_finish(sc_card_t *card) { cac_private_data_t * priv = CAC_DATA(card); SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); if (priv) { cac_free_private_data(priv); } return SC_SUCCESS; } /* select the Card Capabilities Container on CAC-2 */ static int cac_select_CCC(sc_card_t *card) { return cac_select_file_by_type(card, &cac_CCC_Path, NULL); } /* Select ACA in non-standard location */ static int cac_select_ACA(sc_card_t *card) { return cac_select_file_by_type(card, &cac_ACA_Path, NULL); } static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len) { if (len < 10) { return SC_ERROR_INVALID_DATA; } sc_mem_clear(path, sizeof(sc_path_t)); memcpy(path->aid.value, &val->rid, sizeof(val->rid)); memcpy(&path->aid.value[5], val->applicationID, sizeof(val->applicationID)); path->aid.len = sizeof(val->rid) + sizeof(val->applicationID); memcpy(path->value, val->objectID, sizeof(val->objectID)); path->len = sizeof(val->objectID); path->type = SC_PATH_TYPE_FILE_ID; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "path->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x len=%"SC_FORMAT_LEN_SIZE_T"u path->type=%d (%x)", path->aid.value[0], path->aid.value[1], path->aid.value[2], path->aid.value[3], path->aid.value[4], path->aid.value[5], path->aid.value[6], path->aid.len, path->value[0], path->value[1], path->len, path->type, path->type); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "rid=%x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u appid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u objid= %x %x len=%"SC_FORMAT_LEN_SIZE_T"u", val->rid[0], val->rid[1], val->rid[2], val->rid[3], val->rid[4], sizeof(val->rid), val->applicationID[0], val->applicationID[1], sizeof(val->applicationID), val->objectID[0], val->objectID[1], sizeof(val->objectID)); return SC_SUCCESS; } static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, const u8 *aid, int aid_len) { cac_object_t new_object; cac_properties_t prop; size_t i; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Search for PKI applets (7 B). Ignore generic objects for now */ if (aid_len != 7 || (memcmp(aid, CAC_1_RID "\x01", 6) != 0 && memcmp(aid, CAC_1_RID "\x00", 6) != 0)) return SC_SUCCESS; sc_mem_clear(&new_object.path, sizeof(sc_path_t)); memcpy(new_object.path.aid.value, aid, aid_len); new_object.path.aid.len = aid_len; /* Call without OID set will just select the AID without subsequent * OID selection, which we need to figure out just now */ cac_select_file_by_type(card, &new_object.path, NULL); r = cac_get_properties(card, &prop); if (r < 0) return SC_ERROR_INTERNAL; for (i = 0; i < prop.num_objects; i++) { /* don't fail just because we have more certs than we can support */ if (priv->cert_next >= MAX_CAC_SLOTS) return SC_SUCCESS; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA: pki_object found, cert_next=%d (%s), privkey=%d", priv->cert_next, get_cac_label(priv->cert_next), prop.objects[i].privatekey); /* If the private key is not initialized, we can safely * ignore this object here, but increase the pointer to follow * the certificate labels */ if (!prop.objects[i].privatekey) { priv->cert_next++; continue; } /* OID here has always 2B */ memcpy(new_object.path.value, &prop.objects[i].oid, 2); new_object.path.len = 2; new_object.path.type = SC_PATH_TYPE_FILE_ID; new_object.name = get_cac_label(priv->cert_next); new_object.fd = priv->cert_next+1; cac_add_object_to_list(&priv->pki_list, &new_object); priv->cert_next++; } return SC_SUCCESS; } static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card_url_t *val, int len) { cac_object_t new_object; const cac_object_t *obj; unsigned short object_id; int r; r = cac_path_from_cardurl(card, &new_object.path, val, len); if (r != SC_SUCCESS) { return r; } switch (val->cardApplicationType) { case CAC_APP_TYPE_PKI: /* we don't want to overflow the cac_label array. This test could * go way if we create a label function that will create a unique label * from a cert index. */ if (priv->cert_next >= MAX_CAC_SLOTS) break; /* don't fail just because we have more certs than we can support */ new_object.name = get_cac_label(priv->cert_next); new_object.fd = priv->cert_next+1; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name); cac_add_object_to_list(&priv->pki_list, &new_object); priv->cert_next++; break; case CAC_APP_TYPE_GENERAL: object_id = bebytes2ushort(val->objectID); obj = cac_find_obj_by_id(object_id); if (obj == NULL) break; /* don't fail just because we don't recognize the object */ new_object.name = obj->name; new_object.fd = 0; sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: gen_object found, objectID=%x (%s),", object_id, new_object.name); cac_add_object_to_list(&priv->general_list, &new_object); break; case CAC_APP_TYPE_SKI: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: ski_object found"); break; default: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: unknown object_object found (type=0x%02x)", val->cardApplicationType); /* don't fail just because there is an unknown object in the CCC */ break; } return SC_SUCCESS; } static int cac_parse_cuid(sc_card_t *card, cac_private_data_t *priv, cac_cuid_t *val, size_t len) { size_t card_id_len; if (len < sizeof(cac_cuid_t)) { return SC_ERROR_INVALID_DATA; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "gsc_rid=%s", sc_dump_hex(val->gsc_rid, sizeof(val->gsc_rid))); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "manufacture id=%x", val->manufacturer_id); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "cac_type=%d", val->card_type); card_id_len = len - (&val->card_id - (u8 *)val); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "card_id=%s (%"SC_FORMAT_LEN_SIZE_T"u)", sc_dump_hex(&val->card_id, card_id_len), card_id_len); priv->cuid = *val; free(priv->cac_id); priv->cac_id = malloc(card_id_len); if (priv->cac_id == NULL) { return SC_ERROR_OUT_OF_MEMORY; } memcpy(priv->cac_id, &val->card_id, card_id_len); priv->cac_id_len = card_id_len; return SC_SUCCESS; } static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth); static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, const u8 *tl, size_t tl_len, u8 *val, size_t val_len, int depth) { size_t len = 0; const u8 *tl_end = tl + tl_len; const u8 *val_end = val + val_len; sc_path_t new_path; int r; for (; (tl < tl_end) && (val< val_end); val += len) { /* get the tag and the length */ u8 tag; r = sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len); if (r != SC_SUCCESS && r != SC_ERROR_TLV_END_OF_CONTENTS) { sc_log(card->ctx, "Failed to parse tag from buffer"); break; } if (val + len > val_end) { sc_log(card->ctx, "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } switch (tag) { case CAC_TAG_CUID: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CUID"); r = cac_parse_cuid(card, priv, (cac_cuid_t *)val, len); if (r < 0) return r; break; case CAC_TAG_CC_VERSION_NUMBER: if (len != 1) { sc_log(card->ctx, "TAG: CC Version: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } /* ignore the version numbers for now */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: CC Version = 0x%02x", *val); break; case CAC_TAG_GRAMMAR_VERION_NUMBER: if (len != 1) { sc_log(card->ctx, "TAG: Grammar Version: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } /* ignore the version numbers for now */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Grammar Version = 0x%02x", *val); break; case CAC_TAG_CARDURL: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:CARDURL"); r = cac_parse_cardurl(card, priv, (cac_card_url_t *)val, len); if (r < 0) return r; break; /* * The following are really for file systems cards. This code only cares about CAC VM cards */ case CAC_TAG_PKCS15: if (len != 1) { sc_log(card->ctx, "TAG: PKCS15: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } /* TODO should verify that this is '0'. If it's not * zero, we should drop out of here and let the PKCS 15 * code handle this card */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: PKCS15 = 0x%02x", *val); break; case CAC_TAG_DATA_MODEL: if (len != 1) { sc_log(card->ctx, "TAG: Registered Data Model Number: " "Invalid length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: Registered Data Model Number (0x%02x)", *val); break; case CAC_TAG_CARD_APDU: case CAC_TAG_CAPABILITY_TUPLES: case CAC_TAG_STATUS_TUPLES: case CAC_TAG_REDIRECTION: case CAC_TAG_ERROR_CODES: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG: FSSpecific(0x%02x)", tag); break; case CAC_TAG_ACCESS_CONTROL: /* TODO handle access control later */ sc_log_hex(card->ctx, "TAG:ACCESS Control", val, len); break; case CAC_TAG_NEXT_CCC: sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:NEXT CCC"); r = cac_path_from_cardurl(card, &new_path, (cac_card_url_t *)val, len); if (r < 0) return r; r = cac_select_file_by_type(card, &new_path, NULL); if (r < 0) return r; /* Increase depth to avoid infinite recursion */ r = cac_process_CCC(card, priv, depth + 1); if (r < 0) return r; break; default: /* ignore tags we don't understand */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%02x)",tag ); break; } } return SC_SUCCESS; } static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv, int depth) { u8 *tl = NULL, *val = NULL; size_t tl_len, val_len; int r; if (depth > CAC_MAX_CCC_DEPTH) { sc_log(card->ctx, "Too much recursive CCC found. Exiting"); return SC_ERROR_INVALID_CARD; } r = cac_read_file(card, CAC_FILE_TAG, &tl, &tl_len); if (r < 0) goto done; r = cac_read_file(card, CAC_FILE_VALUE, &val, &val_len); if (r < 0) goto done; r = cac_parse_CCC(card, priv, tl, tl_len, val, val_len, depth); done: if (tl) free(tl); if (val) free(val); return r; } /* Service Applet Table (Table 5-21) should list all the applets on the * card, which is a good start if we don't have CCC */ static int cac_parse_ACA_service(sc_card_t *card, cac_private_data_t *priv, const u8 *val, size_t val_len) { size_t len = 0; const u8 *val_end = val + val_len; int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); for (; val < val_end; val += len) { /* get the tag and the length */ u8 tag; if (sc_simpletlv_read_tag(&val, val_end - val, &tag, &len) != SC_SUCCESS) break; switch (tag) { case CAC_TAG_APPLET_FAMILY: if (len != 5) { sc_log(card->ctx, "TAG: Applet Information: " "bad length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Applet Information: Family: 0x%02x", val[0]); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, " Applet Version: 0x%02x 0x%02x 0x%02x 0x%02x", val[1], val[2], val[3], val[4]); break; case CAC_TAG_NUMBER_APPLETS: if (len != 1) { sc_log(card->ctx, "TAG: Num applets: " "bad length %"SC_FORMAT_LEN_SIZE_T"u", len); break; } sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Num applets = %hhd", *val); break; case CAC_TAG_APPLET_ENTRY: /* Make sure we match the outer length */ if (len < 3 || val[2] != len - 3) { sc_log(card->ctx, "TAG: Applet Entry: " "bad length (%"SC_FORMAT_LEN_SIZE_T "u) or length of internal buffer", len); break; } sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Applet Entry: AID", val + 3, val[2]); /* This is SimpleTLV prefixed with applet ID (1B) */ r = cac_parse_aid(card, priv, val + 3, val[2]); if (r < 0) return r; break; default: /* ignore tags we don't understand */ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "TAG: Unknown (0x%02x)", tag); break; } } return SC_SUCCESS; } /* select a CAC pki applet by index */ static int cac_select_pki_applet(sc_card_t *card, int index) { sc_path_t applet_path = cac_cac_pki_obj.path; applet_path.aid.value[applet_path.aid.len-1] = index; return cac_select_file_by_type(card, &applet_path, NULL); } /* * Find the first existing CAC applet. If none found, then this isn't a CAC */ static int cac_find_first_pki_applet(sc_card_t *card, int *index_out) { int r, i; for (i = 0; i < MAX_CAC_SLOTS; i++) { r = cac_select_pki_applet(card, i); if (r == SC_SUCCESS) { /* Try to read first two bytes of the buffer to * make sure it is not just malfunctioning card */ u8 params[2] = {CAC_FILE_TAG, 2}; u8 data[2], *out_ptr = data; size_t len = 2; r = cac_apdu_io(card, CAC_INS_READ_FILE, 0, 0, ¶ms[0], sizeof(params), &out_ptr, &len); if (r != 2) continue; *index_out = i; return SC_SUCCESS; } } return SC_ERROR_OBJECT_NOT_FOUND; } /* * This emulates CCC for Alt tokens, that do not come with CCC nor ACA applets */ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *priv) { int r, i; cac_object_t pki_obj = cac_cac_pki_obj; u8 buf[100]; u8 *val; /* populate PKI objects */ for (i = index; i < MAX_CAC_SLOTS; i++) { r = cac_select_pki_applet(card, i); if (r == SC_SUCCESS) { pki_obj.name = get_cac_label(i); sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name); pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i; pki_obj.fd = i+1; /* don't use id of zero */ cac_add_object_to_list(&priv->pki_list, &pki_obj); } } /* populate non-PKI objects */ for (i=0; i < cac_object_count; i++) { r = cac_select_file_by_type(card, &cac_objects[i].path, NULL); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CAC: obj_object found, cert_next=%d (%s),", i, cac_objects[i].name); cac_add_object_to_list(&priv->general_list, &cac_objects[i]); } } /* * create a cuid to simulate the cac 2 cuid. */ priv->cuid = cac_cac_cuid; /* create a serial number by hashing the first 100 bytes of the * first certificate on the card */ r = cac_select_pki_applet(card, index); if (r < 0) { return r; /* shouldn't happen unless the card has been removed or is malfunctioning */ } val = buf; r = cac_read_binary(card, 0, val, sizeof(buf), 0); if (r > 0) { #ifdef ENABLE_OPENSSL size_t val_len = r; free(priv->cac_id); priv->cac_id = malloc(20); if (priv->cac_id == NULL) { return SC_ERROR_OUT_OF_MEMORY; } SHA1(val, val_len, priv->cac_id); priv->cac_id_len = 20; sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE, "cuid", priv->cac_id, priv->cac_id_len); #else sc_log(card->ctx, "OpenSSL Required"); return SC_ERROR_NOT_SUPPORTED; #endif /* ENABLE_OPENSSL */ } return SC_SUCCESS; } static int cac_process_ACA(sc_card_t *card, cac_private_data_t *priv) { int r; u8 *val = NULL; size_t val_len; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Assuming ACA is already selected */ r = cac_get_acr(card, CAC_ACR_SERVICE, &val, &val_len); if (r < 0) goto done; r = cac_parse_ACA_service(card, priv, val, val_len); if (r == SC_SUCCESS) { priv->aca_path = malloc(sizeof(sc_path_t)); if (!priv->aca_path) { r = SC_ERROR_OUT_OF_MEMORY; goto done; } memcpy(priv->aca_path, &cac_ACA_Path, sizeof(sc_path_t)); } done: if (val) free(val); LOG_FUNC_RETURN(card->ctx, r); } /* * Look for a CAC card. If it exists, initialize our data structures */ static int cac_find_and_initialize(sc_card_t *card, int initialize) { int r, index; cac_private_data_t *priv = NULL; /* already initialized? */ if (card->drv_data) { return SC_SUCCESS; } /* is this a CAC-2 specified in NIST Interagency Report 6887 - * "Government Smart Card Interoperability Specification v2.1 July 2003" */ r = cac_select_CCC(card); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "CCC found, is CAC-2"); if (!initialize) /* match card only */ return r; priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; r = cac_process_CCC(card, priv, 0); if (r == SC_SUCCESS) { card->type = SC_CARD_TYPE_CAC_II; card->drv_data = priv; return r; } } /* Even some ALT tokens can be missing CCC so we should try with ACA */ r = cac_select_ACA(card); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "ACA found, is CAC-2 without CCC"); if (!initialize) /* match card only */ return r; if (!priv) { priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; } r = cac_process_ACA(card, priv); if (r == SC_SUCCESS) { card->type = SC_CARD_TYPE_CAC_II; card->drv_data = priv; return r; } } /* is this a CAC Alt token without any accompanying structures */ r = cac_find_first_pki_applet(card, &index); if (r == SC_SUCCESS) { sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC Alt"); if (!initialize) /* match card only */ return r; if (!priv) { priv = cac_new_private_data(); if (!priv) return SC_ERROR_OUT_OF_MEMORY; } card->drv_data = priv; /* needed for the read_binary() */ r = cac_populate_cac_alt(card, index, priv); if (r == SC_SUCCESS) { card->type = SC_CARD_TYPE_CAC_II; return r; } card->drv_data = NULL; /* reset on failure */ } if (priv) { cac_free_private_data(priv); } return r; } /* NOTE: returns a bool, 1 card matches, 0 it does not */ static int cac_match_card(sc_card_t *card) { int r; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); /* Since we send an APDU, the card's logout function may be called... * however it may be in dirty memory */ card->ops->logout = NULL; r = cac_find_and_initialize(card, 0); return (r == SC_SUCCESS); /* never match */ } static int cac_init(sc_card_t *card) { int r; unsigned long flags; SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); r = cac_find_and_initialize(card, 1); if (r < 0) { LOG_FUNC_RETURN(card->ctx, SC_ERROR_INVALID_CARD); } 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 */ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); } static int cac_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { /* CAC, like PIV needs Extra validation of (new) PIN during * a PIN change request, to ensure it's not outside the * FIPS 201 4.1.6.1 (numeric only) and * FIPS 140-2 * (6 character minimum) requirements. */ struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); if (data->cmd == SC_PIN_CMD_CHANGE) { int i = 0; if (data->pin2.len < 6) { return SC_ERROR_INVALID_PIN_LENGTH; } for(i=0; i < data->pin2.len; ++i) { if (!isdigit(data->pin2.data[i])) { return SC_ERROR_INVALID_DATA; } } } return iso_drv->ops->pin_cmd(card, data, tries_left); } static struct sc_card_operations cac_ops; static struct sc_card_driver cac_drv = { "Common Access Card (CAC)", "cac", &cac_ops, NULL, 0, NULL }; static struct sc_card_driver * sc_get_driver(void) { struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); cac_ops = *iso_drv->ops; cac_ops.match_card = cac_match_card; cac_ops.init = cac_init; cac_ops.finish = cac_finish; cac_ops.select_file = cac_select_file; /* need to record object type */ cac_ops.get_challenge = cac_get_challenge; cac_ops.read_binary = cac_read_binary; /* CAC driver is read only */ cac_ops.write_binary = NULL; cac_ops.set_security_env = cac_set_security_env; cac_ops.restore_security_env = cac_restore_security_env; cac_ops.compute_signature = cac_compute_signature; cac_ops.decipher = cac_decipher; cac_ops.card_ctl = cac_card_ctl; cac_ops.pin_cmd = cac_pin_cmd; return &cac_drv; } struct sc_card_driver * sc_get_cac_driver(void) { return sc_get_driver(); }