opensc/src/libopensc/card-cac.c

1927 lines
54 KiB
C

/*
* 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ä <juha.yrjola@iki.fi>
* Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
* Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
* Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
* Copyright (C) 2016 - 2018, Red Hat, Inc.
*
* CAC driver author: Robert Relyea <rrelyea@redhat.com>
* Further work: Jakub Jelen <jjelen@redhat.com>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#if HAVE_CONFIG_H
#include "config.h"
#endif
#include <ctype.h>
#include <fcntl.h>
#include <limits.h>
#include <stdlib.h>
#include <string.h>
#ifdef _WIN32
#include <io.h>
#else
#include <unistd.h>
#endif
#ifdef ENABLE_OPENSSL
#include <openssl/sha.h>
#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, &params[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),
&params[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,
&params[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();
}