[pkg-opensc-commit] [opensc] 179/295: adding a CAC support into OpenSC (#841)
Eric Dorland
eric at moszumanska.debian.org
Sat Jun 24 21:11:29 UTC 2017
This is an automated email from the git hooks/post-receive script.
eric pushed a commit to branch master
in repository opensc.
commit 777e2a3751e3f6d53f056c98e9e20e42af674fb1
Author: Jakuje <jakuje at gmail.com>
Date: Mon Feb 27 11:05:12 2017 +0100
adding a CAC support into OpenSC (#841)
* Includes adding support for parsing extensions from a certificate.
* Move lebytes2ushort() to related functions in internals.h
* Adds Simple TLV related functions
---
src/common/simclist.h | 2 +-
src/libopensc/Makefile.am | 12 +-
src/libopensc/Makefile.mak | 10 +-
src/libopensc/card-cac.c | 1555 +++++++++++++++++++++++++++++++++++++++++++
src/libopensc/cardctl.h | 11 +
src/libopensc/cards.h | 8 +-
src/libopensc/ctx.c | 1 +
src/libopensc/internal.h | 7 +
src/libopensc/pkcs15-cac.c | 436 ++++++++++++
src/libopensc/pkcs15-cert.c | 5 +-
src/libopensc/pkcs15-syn.c | 1 +
src/libopensc/pkcs15-syn.h | 1 +
src/libopensc/sc.c | 7 +
src/libopensc/simpletlv.c | 99 +++
src/libopensc/simpletlv.h | 54 ++
15 files changed, 2196 insertions(+), 13 deletions(-)
diff --git a/src/common/simclist.h b/src/common/simclist.h
index 3fd5254..49ac31f 100644
--- a/src/common/simclist.h
+++ b/src/common/simclist.h
@@ -124,7 +124,7 @@ typedef int (*element_comparator)(const void *a, const void *b);
typedef int (*element_seeker)(const void *el, const void *indicator);
/**
- * an element lenght meter.
+ * an element length meter.
*
* An element meter is a function that:
* -# receives the reference to an element el
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index a31d5e6..4f39f65 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -8,7 +8,7 @@ lib_LTLIBRARIES = libopensc.la
noinst_HEADERS = cards.h ctbcs.h internal.h esteid.h muscle.h muscle-filesystem.h \
internal-winscard.h p15card-helper.h pkcs15-syn.h \
opensc.h pkcs15.h \
- cardctl.h asn1.h log.h \
+ cardctl.h asn1.h log.h simpletlv.h \
errors.h types.h compression.h itacns.h iso7816.h \
authentic.h iasecc.h iasecc-sdo.h sm.h card-sc-hsm.h \
pace.h cwa14890.h cwa-dnie.h card-gids.h aux-data.h \
@@ -22,6 +22,7 @@ AM_CFLAGS = $(OPTIONAL_OPENSSL_CFLAGS) $(OPTIONAL_OPENCT_CFLAGS) \
libopensc_la_SOURCES = \
sc.c ctx.c log.c errors.c \
asn1.c base64.c sec.c card.c iso7816.c dir.c ef-atr.c padding.c apdu.c \
+ simpletlv.c \
\
pkcs15.c pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \
pkcs15-prkey.c pkcs15-pubkey.c pkcs15-skey.c \
@@ -35,8 +36,8 @@ libopensc_la_SOURCES = \
card-cardos.c card-tcos.c card-default.c \
card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \
card-oberthur.c card-belpic.c card-atrust-acos.c \
- card-entersafe.c card-epass2003.c card-coolkey.c \
- card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \
+ card-entersafe.c card-epass2003.c card-coolkey.c card-incrypto34.c \
+ card-piv.c card-cac.c card-muscle.c card-acos5.c \
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
card-rtecp.c card-westcos.c card-myeid.c \
card-itacns.c card-authentic.c \
@@ -47,8 +48,9 @@ libopensc_la_SOURCES = \
pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \
pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \
pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \
- pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c pkcs15-oberthur.c \
- pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c pkcs15-coolkey.c \
+ pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \
+ pkcs15-oberthur.c pkcs15-itacns.c pkcs15-gemsafeV1.c pkcs15-sc-hsm.c \
+ pkcs15-coolkey.c \
pkcs15-dnie.c pkcs15-gids.c pkcs15-iasecc.c pkcs15-jpki.c \
compression.c p15card-helper.c sm.c \
aux-data.c \
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 741be19..e369ec6 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -3,7 +3,8 @@ TOPDIR = ..\..
TARGET = opensc.dll opensc_a.lib
OBJECTS = \
sc.obj ctx.obj log.obj errors.obj \
- asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj padding.obj apdu.obj \
+ asn1.obj base64.obj sec.obj card.obj iso7816.obj dir.obj ef-atr.obj \
+ padding.obj apdu.obj simpletlv.obj \
\
pkcs15.obj pkcs15-cert.obj pkcs15-data.obj pkcs15-pin.obj \
pkcs15-prkey.obj pkcs15-pubkey.obj pkcs15-skey.obj \
@@ -18,7 +19,8 @@ OBJECTS = \
card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \
card-oberthur.obj card-belpic.obj card-atrust-acos.obj \
card-entersafe.obj card-epass2003.obj card-coolkey.obj \
- card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \
+ card-incrypto34.obj card-cac.obj card-piv.obj card-muscle.obj \
+ card-acos5.obj \
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
card-rtecp.obj card-westcos.obj card-myeid.obj \
card-itacns.obj card-authentic.obj \
@@ -29,8 +31,8 @@ OBJECTS = \
pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \
pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \
pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \
- pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-oberthur.obj \
- pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
+ pkcs15-cac.obj pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj \
+ pkcs15-oberthur.obj pkcs15-itacns.obj pkcs15-gemsafeV1.obj pkcs15-sc-hsm.obj \
pkcs15-dnie.obj pkcs15-gids.obj pkcs15-iasecc.obj pkcs15-jpki.obj \
compression.obj p15card-helper.obj sm.obj \
aux-data.obj \
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
new file mode 100644
index 0000000..d5f8585
--- /dev/null
+++ b/src/libopensc/card-cac.c
@@ -0,0 +1,1555 @@
+/*
+ * 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 at iki.fi>
+ * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert at anl.gov>
+ * Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning at identityalliance.com>
+ * Copyright (C) 2007, EMC, Russell Larner <rlarner at rsa.com>
+ * Copyright (C) 2016, Red Hat, Inc.
+ *
+ * CAC driver author: Robert Relyea <rrelyea at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <ctype.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#ifdef ENABLE_OPENSSL
+ /* openssl only needed for card administration */
+#include <openssl/evp.h>
+#include <openssl/bio.h>
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#endif /* ENABLE_OPENSSL */
+
+#include "internal.h"
+#include "simpletlv.h"
+#include "cardctl.h"
+#ifdef ENABLE_ZLIB
+#include "compression.h"
+#endif
+#include "iso7816.h"
+
+#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
+/*
+ * CAC hardware and APDU constants
+ */
+#define CAC_MAX_CHUNK_SIZE 240
+#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */
+#define CAC_INS_SIGN_DECRYPT 0x42 /* A crypto operation */
+#define CAC_P1_STEP 0x80
+#define CAC_P1_FINAL 0x00
+#define CAC_INS_READ_FILE 0x52 /* read a TL or V file */
+#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_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_APP_TYPE_GENERAL 0x01
+#define CAC_APP_TYPE_SKI 0x02
+#define CAC_APP_TYPE_PKI 0x04
+
+/* 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;
+
+typedef struct cac_cuid {
+ u8 gsc_rid[5];
+ u8 manufacturer_id;
+ u8 card_type;
+ u8 card_id;
+} cac_cuid_t;
+
+/* data structures to store meta data about CAC objects */
+typedef struct cac_object {
+ const char *name;
+ int fd;
+ sc_path_t path;
+} cac_object_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
+
+/*
+ * CAC private data per card state
+ */
+typedef struct cac_private_data {
+ int object_type; /* select set this so we know how to read the file */
+ int cert_next; /* index number for the next certificate found in the list */
+ u8 *cache_buf; /* cached version of the currently selected file */
+ size_t cache_buf_len; /* length of the cached selected file */
+ int cached; /* is the cached selected file valid */
+ cac_cuid_t cuid; /* card unique ID from the CCC */
+ u8 *cac_id; /* card serial number */
+ size_t cac_id_len; /* card serial number len */
+ list_t pki_list; /* list of pki containers */
+ cac_object_t *pki_current; /* current pki object _ctl function */
+ list_t general_list; /* list of general containers */
+ cac_object_t *general_current; /* current object for _ctl function */
+} cac_private_data_t;
+
+#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
+
+int cac_list_compare_path(const void *a, const void *b)
+{
+ if (a == NULL || b == NULL)
+ return 1;
+ return memcmp( &((cac_object_t *) a)->path,
+ &((cac_object_t *) b)->path, sizeof(sc_path_t));
+}
+
+/* For SimCList autocopy, we need to know the size of the data elements */
+size_t cac_list_meter(const void *el) {
+ return sizeof(cac_object_t);
+}
+
+static cac_private_data_t *cac_new_private_data(void)
+{
+ cac_private_data_t *priv;
+ priv = calloc(1, sizeof(cac_private_data_t));
+ list_init(&priv->pki_list);
+ list_attributes_comparator(&priv->pki_list, cac_list_compare_path);
+ list_attributes_copy(&priv->pki_list, cac_list_meter, 1);
+ list_init(&priv->general_list);
+ list_attributes_comparator(&priv->general_list, cac_list_compare_path);
+ list_attributes_copy(&priv->general_list, cac_list_meter, 1);
+ /* set other fields as appropriate */
+
+ return priv;
+}
+
+static void cac_free_private_data(cac_private_data_t *priv)
+{
+ free(priv->cac_id);
+ free(priv->cache_buf);
+ list_destroy(&priv->pki_list);
+ list_destroy(&priv->general_list);
+ free(priv);
+ return;
+}
+
+static int cac_add_object_to_list(list_t *list, const cac_object_t *object)
+{
+ if (list_append(list, object) < 0)
+ return SC_ERROR_UNKNOWN;
+ return SC_SUCCESS;
+}
+
+/*
+ * Set up the normal CAC paths
+ */
+#define CAC_TO_AID(x) x, sizeof(x)-1
+
+#define CAC_2_RID "\xA0\x00\x00\x01\x16"
+#define CAC_1_RID "\xA0\x00\x00\x00\x79"
+#define CAC_1_CM_AID "\xA0\x00\x00\x00\x30\x00\00"
+
+static const sc_path_t cac_CCC_Path = {
+ "", 0,
+ 0,0,SC_PATH_TYPE_DF_NAME,
+ { CAC_TO_AID(CAC_2_RID "\xDB\x00") }
+};
+
+#define MAX_CAC_SLOTS 10 /* arbitrary, just needs to be 'large enough' */
+/* default certificate labels for the CAC card */
+static const char *cac_labels[MAX_CAC_SLOTS] = {
+ "CAC ID Certificate",
+ "CAC Email Signature Certificate",
+ "CAC Email Encryption Certificate",
+ "CAC Cert 3",
+ "CAC Cert 4",
+ "CAC Cert 5",
+ "CAC Cert 6",
+ "CAC Cert 7",
+ "CAC Cert 8",
+ "CAC Cert 9"
+};
+
+/* template for a cac1 pki object */
+static const cac_object_t cac_cac1_pki_obj = {
+ "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+ { CAC_TO_AID(CAC_1_RID "\x01\x00") } }
+};
+
+/* template for cac1 cuid */
+static const cac_cuid_t cac_cac1_cuid = {
+ { 0xa0, 0x00, 0x00, 0x00, 0x79 },
+ 2, 2, 0
+};
+
+/*
+ * CAC-1 general objectes 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_1_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", 0x202, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+ { CAC_TO_AID(CAC_1_RID "\x02\x02") }}},
+ { "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_1_object_count = sizeof(cac_1_objects)/sizeof(cac_1_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_1_object_count; i++) {
+ if (cac_1_objects[i].fd == object_id) {
+ return &cac_1_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 similiar 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;
+
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "%02x %02x %02x %d : %d %d\n",
+ ins, p1, p2, sendbuflen, card->max_send_size, card->max_recv_size);
+
+ rbuf = rbufinitbuf;
+ rbuflen = sizeof(rbufinitbuf);
+
+ /* if caller provided a buffer and length */
+ if (recvbuf && *recvbuf && recvbuflen && *recvbuflen) {
+ rbuf = *recvbuf;
+ rbuflen = *recvbuflen;
+ }
+
+ sc_format_apdu(card, &apdu,
+ recvbuf ? SC_APDU_CASE_4_SHORT: SC_APDU_CASE_3_SHORT,
+ 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p",
+ apdu.flags, apdu.le, apdu.resplen, apdu.resp);
+
+ /* with new adpu.c and chaining, this actually reads the whole object */
+ r = sc_transmit_apdu(card, &apdu);
+
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"result r=%d apdu.resplen=%d sw1=%02x sw2=%02x",
+ r, apdu.resplen, apdu.sw1, apdu.sw2);
+ if (r < 0) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Transmit failed");
+ goto err;
+ }
+
+ if (apdu.sw1 == 0x61) {
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ }
+
+ if (r < 0) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "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:
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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 (r < 0)
+ goto fail;
+
+ left = size = lebytes2ushort(count);
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "got %d bytes out_ptr=%lx count&=%lx count[0]=0x%02x count[1]=0x%02x, len=0x%04x (%d)",
+ len, (unsigned long) out_ptr, (unsigned long)&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 (r < 0) {
+ goto fail;
+ }
+ }
+ *out_len = size;
+ *out_buf = out;
+ return SC_SUCCESS;
+fail:
+ if (out)
+ free(out);
+ *out_len = 0;
+ return r;
+}
+
+/*
+ * OLD cac read certificate, only use with CAC-1 card.
+ */
+static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len)
+{
+ u8 buf[CAC_MAX_SIZE];
+ u8 *out_ptr;
+ size_t size = 0;
+ size_t left = 0;
+ size_t len, next_len;
+ sc_apdu_t apdu;
+ int r;
+
+
+ /* get the size */
+ size = left = *out_buf ? *out_len : sizeof(buf);
+ out_ptr = *out_buf ? *out_buf : buf;
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 );
+ next_len = MIN(left, 100);
+ for (; left > 0; left -= len, out_ptr += len) {
+ len = next_len;
+ apdu.resp = out_ptr;
+ apdu.le = len;
+ apdu.resplen = left;
+
+ r = sc_transmit_apdu(card, &apdu);
+ if (r < 0) {
+ break;
+ }
+ /* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */
+ if (apdu.sw1 != 0x63) {
+ /* we've either finished reading, or hit an error, break */
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
+ left -= len;
+ break;
+ }
+ next_len = MIN(left,apdu.sw2);
+ }
+ if (r < 0) {
+ return r;
+ }
+ r = size - left;
+ if (*out_buf == NULL) {
+ *out_buf = malloc(r);
+ if (*out_buf == NULL) {
+ return SC_ERROR_OUT_OF_MEMORY;
+ }
+ memcpy(*out_buf, buf, r);
+ }
+ *out_len = r;
+ return r;
+}
+
+/* Create a fake tag/length file in Simple TLV for cac1 cards based on the val_len.
+ */
+int cac_cac1_get_cert_tag(sc_card_t *card, size_t val_len, u8 **tlp, size_t *tl_len_p)
+{
+ static const u8 cac_cac1_cert_tag[] = { CAC_TAG_CERTINFO, 1, CAC_TAG_CERTIFICATE, 0xff, 0, 0 };
+ u8 *tl, *tl1, *tl2;
+ size_t tl_len;
+ int rv = SC_SUCCESS;
+
+ tl_len = sizeof(cac_cac1_cert_tag);
+ tl = malloc(tl_len);
+ if (tl == NULL)
+ return SC_ERROR_OUT_OF_MEMORY;
+ memcpy(tl, cac_cac1_cert_tag, tl_len);
+
+ rv = sc_simpletlv_put_tag(CAC_TAG_CERTINFO, 1, tl, tl_len, &tl1);
+ if (rv != SC_SUCCESS)
+ goto failure;
+
+ val_len -= 1; /* one byte is CERTINFO Value */
+ tl_len -= (tl1 - tl);
+ rv = sc_simpletlv_put_tag(CAC_TAG_CERTIFICATE, val_len, tl1, tl_len, &tl2);
+ if (rv != SC_SUCCESS)
+ goto failure;
+
+ *tlp = tl;
+ *tl_len_p = (tl2 - tl);
+ return SC_SUCCESS;
+failure:
+ free(tl);
+ return rv;
+}
+
+/*
+ * 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;
+ u8 *tl_ptr, *val_ptr, *tlv_ptr, *tl_start;
+ 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"returning cached value idx=%d count=%d",idx, count);
+ if (idx > priv->cache_buf_len) {
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED);
+ }
+ len = MIN(count, priv->cache_buf_len-idx);
+ memcpy(buf, &priv->cache_buf[idx], len);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len);
+ }
+
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"clearing cache idx=%d count=%d",idx, count);
+ if (priv->cache_buf) {
+ free(priv->cache_buf);
+ priv->cache_buf = NULL;
+ priv->cache_buf_len = 0;
+ }
+
+
+ if (priv->object_type <= 0)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INTERNAL);
+
+ if ((card->type == SC_CARD_TYPE_CAC_I) && (priv->object_type == CAC_OBJECT_TYPE_CERT)) {
+ /* SPICE smart card emulator only presents CAC-1 cards with the old CAC-1 interface as
+ * certs. If we are a cac 1 card, use the old interface */
+ r = cac_cac1_get_certificate(card, &val, &val_len);
+ if (r < 0)
+ goto done;
+
+ r = cac_cac1_get_cert_tag(card, val_len, &tl, &tl_len);
+ if (r < 0)
+ goto done;
+ } else {
+ 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 && val_len > 0 && 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;
+ if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
+ 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) {
+ 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL," obj= cert_file, val_len=%d (0x%04x)", val_len, val_len);
+ cert_len = 0;
+ cert_ptr = NULL;
+ cert_type = 0;
+ tl_head_len = 2;
+ 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;
+ if (sc_simpletlv_read_tag(&tl_ptr, tl_len, &tag, &len) != SC_SUCCESS)
+ break;
+ tl_head_len = tl_ptr - tl_start;
+ 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 ((val_len < len) || (tl_len < tl_head_len)) {
+ break;
+ }
+ }
+ /* 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, "PIV compression not supported, no zlib");
+ r = SC_ERROR_NOT_SUPPORTED;
+#endif
+ if (r)
+ goto done;
+ } else {
+ 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);
+ }
+ break;
+ default:
+ /* Unknown 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);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+/* CAC driver is read only */
+static int cac_write_binary(sc_card_t *card, unsigned int idx,
+ const u8 *buf, size_t count, unsigned long flags)
+{
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_NOT_SUPPORTED);
+}
+
+/* initialize getting a list and return the number of elements in the list */
+static int 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);
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_NORMAL);
+ if (card->serialnr.len) {
+ *serial = card->serialnr;
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+ }
+ if (priv->cac_id_len) {
+ serial->len = MIN(priv->cac_id_len, SC_MAX_SERIALNR);
+ memcpy(serial->value, priv->cac_id, priv->cac_id_len);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+ }
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_NOT_FOUND);
+}
+
+
+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_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)
+{
+ u8 rbuf[8];
+ u8 *rbufp = NULL;
+ size_t rbuflen = 0;
+ int r;
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"challenge len=%d",len);
+
+ r = sc_lock(card);
+ if (r != SC_SUCCESS)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+
+ /* CAC requires 8 byte response */
+ while (len > 0) {
+ size_t n;
+
+ rbufp = &rbuf[0];
+ rbuflen = sizeof(rbuf);
+ r = cac_apdu_io(card, 0x84, 0x00, 0x00, NULL, 0, &rbufp, &rbuflen);
+ if (r < 0) {
+ sc_unlock(card);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+ }
+ n = len > rbuflen ? rbuflen : len;
+ memcpy(rnd, rbufp, n);
+ len -= n;
+ rnd += n;
+ }
+
+ r = sc_unlock(card);
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+
+}
+
+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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"flags=%08x op=%d alg=%d algf=%08x algr=%08x kr0=%02x, krfl=%d\n",
+ env->flags, env->operation, env->algorithm, env->algorithm_flags,
+ env->algorithm_ref, env->key_ref[0], env->key_ref_len);
+
+ if (env->algorithm != SC_ALGORITHM_RSA) {
+ 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);
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"datalen=%d outlen=%d\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)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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;
+ 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);
+ }
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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));
+}
+
+/*
+ * 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, int type)
+{
+ 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->aid=%x %x %x %x %x %x %x len=%d, path->value = %x %x %x %x len=%d path->type=%d (%x)",
+ in_path->aid.value[0], in_path->aid.value[1], in_path->aid.value[2], in_path->aid.value[3],
+ in_path->aid.value[4], in_path->aid.value[5], in_path->aid.value[6], in_path->aid.len,
+ in_path->value[0], in_path->value[1], in_path->value[2], in_path->value[3], in_path->len,
+ in_path->type, in_path->type);
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"file_out=%lx index=%d count=%d\n",(unsigned long) file_out,
+ in_path->index, in_path->count);
+
+ /* Sigh, sc_key_select 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. A better fix would be to give sc_key_file
+ * a flag that says 'no, really this path is fine'. We only need to do this for private keys */
+ if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) {
+ if (pathlen > 2) {
+ 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_TLV_FILE;
+ if (cac_is_cert(priv, in_path)) {
+ priv->object_type = CAC_OBJECT_TYPE_CERT;
+ }
+ /* forget any old cacheed 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 = (type == SC_CARD_TYPE_CAC_I)? 0x00 : 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;
+ 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);
+
+ /* CAC cards enver 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;
+ SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, 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, card->type);
+}
+
+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, SC_CARD_TYPE_CAC_II);
+}
+
+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=%d, path->value = %x %x len=%d 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=%d appid= %x %x len=%d objid= %x %x len=%d",
+ 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_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 = cac_labels[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: unkown object_object found (type=0x%x)", 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 (%d)",sc_dump_hex(&val->card_id, card_id_len),card_id_len);
+ priv->cuid = *val;
+ 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);
+
+static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl,
+ size_t tl_len, u8 *val, size_t val_len)
+{
+ size_t len = 0;
+ u8 *tl_end = tl + tl_len;
+ 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;
+ if (sc_simpletlv_read_tag(&tl, tl_end - tl, &tag, &len) != SC_SUCCESS)
+ 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:
+ case CAC_TAG_GRAMMAR_VERION_NUMBER:
+ /* ignore the version numbers for now */
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Version");
+ 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:
+ /* 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:PKCS5");
+ break;
+ case CAC_TAG_DATA_MODEL:
+ 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%x)", tag);
+ break;
+ case CAC_TAG_ACCESS_CONTROL:
+ /* handle access control later */
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:ACCESS Control");
+ 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, SC_CARD_TYPE_CAC_II);
+ if (r < 0)
+ return r;
+
+ r = cac_process_CCC(card, priv);
+ if (r < 0)
+ return r;
+ break;
+ default:
+ /* ignore tags we don't understand */
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"TAG:Unknown (0x%x)",tag );
+ break;
+ }
+ }
+ return SC_SUCCESS;
+}
+
+static int cac_process_CCC(sc_card_t *card, cac_private_data_t *priv)
+{
+ u8 *tl = NULL, *val = NULL;
+ size_t tl_len, val_len;
+ int r;
+
+
+ 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);
+done:
+ if (tl)
+ free(tl);
+ if (val)
+ free(val);
+ return r;
+}
+
+/* select a CAC-1 pki applet by index */
+static int cac_select_pki_applet(sc_card_t *card, int index)
+{
+ sc_path_t applet_path = cac_cac1_pki_obj.path;
+ applet_path.aid.value[applet_path.aid.len-1] = index;
+ return cac_select_file_by_type(card, &applet_path, NULL, SC_CARD_TYPE_CAC_I);
+}
+
+/*
+ * Find the first existing CAC-1 applet. If none found, then this isn't a CAC-1
+ */
+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) {
+ *index_out = i;
+ return r;
+ }
+ }
+ return SC_ERROR_OBJECT_NOT_FOUND;
+}
+
+/*
+ * CAC-1 has been found, identify all the certs and general containers.
+ * This emulates CAC-2's CCC.
+ */
+static int cac_populate_cac_1(sc_card_t *card, int index, cac_private_data_t *priv)
+{
+ int r, i;
+ cac_object_t pki_obj = cac_cac1_pki_obj;
+ u8 buf[100];
+ u8 *val;
+ size_t val_len;
+
+ /* 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 = cac_labels[i];
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: 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_1_object_count; i++) {
+ r = cac_select_file_by_type(card, &cac_1_objects[i].path, NULL, SC_CARD_TYPE_CAC_I);
+ if (r == SC_SUCCESS) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CAC1: obj_object found, cert_next=%d (%s),", i, cac_1_objects[i].name);
+ cac_add_object_to_list(&priv->general_list, &cac_1_objects[i]);
+ }
+ }
+
+ /*
+ * create a cuid to simulate the cac 2 cuid.
+ */
+ priv->cuid = cac_cac1_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;
+ val_len = sizeof(buf);
+ r = cac_cac1_get_certificate(card, &val, &val_len);
+ if (r >= 0) {
+ priv->cac_id = malloc(20);
+ if (priv->cac_id == NULL) {
+ return SC_ERROR_OUT_OF_MEMORY;
+ }
+#ifdef ENABLE_OPENSSL
+ SHA1(val, val_len, priv->cac_id);
+ priv->cac_id_len = 20;
+#else
+ sc_log(card->ctx, "OpenSSL Required");
+ return SC_ERROR_NOT_SUPPORTED;
+#endif /* ENABLE_OPENSSL */
+ }
+ return SC_SUCCESS;
+}
+
+/*
+ * 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();
+ r = cac_process_CCC(card, priv);
+ if (r == SC_SUCCESS) {
+ card->type = SC_CARD_TYPE_CAC_II;
+ card->drv_data = priv;
+ return r;
+ }
+ }
+
+ /* is this a CAC-1 specified in DoD "CAC Applet Developer Guide" version 1.0 September 2002 */
+ r = cac_find_first_pki_applet(card, &index);
+ if (r == SC_SUCCESS) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "applet found, is CAC-1");
+ if (!initialize) /* match card only */
+ return r;
+
+ if (!priv) {
+ priv = cac_new_private_data();
+ }
+ r = cac_populate_cac_1(card, index, priv);
+ if (r == SC_SUCCESS) {
+ card->type = SC_CARD_TYPE_CAC_I;
+ card->drv_data = priv;
+ return r;
+ }
+ }
+ 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) {
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+ }
+ flags = SC_ALGORITHM_RSA_RAW;
+
+ _sc_card_add_rsa_alg(card, 1024, flags, 0); /* manditory */
+ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
+ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
+
+ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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_ops.write_binary = cac_write_binary;
+ 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();
+}
diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h
index 22c654c..d67a83c 100644
--- a/src/libopensc/cardctl.h
+++ b/src/libopensc/cardctl.h
@@ -210,6 +210,17 @@ enum {
SC_CARDCTL_PIV_PIN_PREFERENCE,
SC_CARDCTL_PIV_OBJECT_PRESENT,
+ /*
+ * CAC specific calls
+ */
+ SC_CARDCTL_CAC_BASE = _CTL_PREFIX('C', 'A', 'C'),
+ SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS,
+ SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT,
+ SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS,
+ SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS,
+ SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT,
+ SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS,
+
/*
* AuthentIC v3
*/
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index d0ce788..d71c02f 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -228,9 +228,14 @@ enum {
/* JPKI cards */
SC_CARD_TYPE_JPKI_BASE = 31000,
- /* CAC cards */
SC_CARD_TYPE_COOLKEY_BASE = 32000,
SC_CARD_TYPE_COOLKEY_GENERIC,
+
+ /* CAC cards */
+ SC_CARD_TYPE_CAC_BASE = 33000,
+ SC_CARD_TYPE_CAC_GENERIC,
+ SC_CARD_TYPE_CAC_I,
+ SC_CARD_TYPE_CAC_II,
};
extern sc_card_driver_t *sc_get_default_driver(void);
@@ -271,6 +276,7 @@ extern sc_card_driver_t *sc_get_masktech_driver(void);
extern sc_card_driver_t *sc_get_gids_driver(void);
extern sc_card_driver_t *sc_get_jpki_driver(void);
extern sc_card_driver_t *sc_get_coolkey_driver(void);
+extern sc_card_driver_t *sc_get_cac_driver(void);
#ifdef __cplusplus
}
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index bf0a618..9f296b6 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -111,6 +111,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
{ "muscle", (void *(*)(void)) sc_get_muscle_driver },
{ "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver },
{ "PIV-II", (void *(*)(void)) sc_get_piv_driver },
+ { "cac", (void *(*)(void)) sc_get_cac_driver },
{ "itacns", (void *(*)(void)) sc_get_itacns_driver },
{ "isoApplet", (void *(*)(void)) sc_get_isoApplet_driver },
#ifdef ENABLE_ZLIB
diff --git a/src/libopensc/internal.h b/src/libopensc/internal.h
index 98a4237..efc076a 100644
--- a/src/libopensc/internal.h
+++ b/src/libopensc/internal.h
@@ -111,6 +111,13 @@ unsigned long bebytes2ulong(const u8 *buf);
*/
unsigned short bebytes2ushort(const u8 *buf);
+/**
+ * Convert 2 bytes in little endian order into an unsigned short
+ * @param buf the byte array of 2 bytes
+ * @return the converted value
+ */
+unsigned short lebytes2ushort(const u8 *buf);
+
/* Returns an scconf_block entry with matching ATR/ATRmask to the ATR specified,
* NULL otherwise. Additionally, if card driver is not specified, search through
* all card drivers user configured ATRs. */
diff --git a/src/libopensc/pkcs15-cac.c b/src/libopensc/pkcs15-cac.c
new file mode 100644
index 0000000..4894fe4
--- /dev/null
+++ b/src/libopensc/pkcs15-cac.c
@@ -0,0 +1,436 @@
+/*
+ * partial PKCS15 emulation for CAC-II cards
+ * only minimal use of the authentication cert and key
+ *
+ * Copyright (C) 2005,2006,2007,2008,2009,2010
+ * Douglas E. Engert <deengert at anl.gov>
+ * 2004, Nils Larsch <larsch at trustcenter.de>
+ * Copyright (C) 2006, Identity Alliance,
+ * Thomas Harning <thomas.harning at identityalliance.com>
+ * Copyright (C) 2007, EMC, Russell Larner <rlarner at rsa.com>
+ * Copyright (C) 2016, Red Hat, Inc.
+ *
+ * CAC driver author: Robert Relyea <rrelyea at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <ctype.h>
+
+#include "internal.h"
+#include "cardctl.h"
+#include "pkcs15.h"
+/* X509 Key Usage flags */
+#include "../pkcs15init/pkcs15-init.h"
+
+/* probably should get manufacturer ID from cuid */
+#define MANU_ID "Common Access Card"
+
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *);
+
+
+
+typedef struct pdata_st {
+ const char *id;
+ const char *label;
+ const char *path;
+ int ref;
+ int type;
+ unsigned int maxlen;
+ unsigned int minlen;
+ unsigned int storedlen;
+ int flags;
+ int tries_left;
+ const unsigned char pad_char;
+ int obj_flags;
+} pindata;
+
+static int cac_detect_card(sc_pkcs15_card_t *p15card)
+{
+ sc_card_t *card = p15card->card;
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+ if (card->type < SC_CARD_TYPE_CAC_GENERIC
+ || card->type >= SC_CARD_TYPE_CAC_GENERIC+1000)
+ return SC_ERROR_INVALID_CARD;
+ return SC_SUCCESS;
+}
+
+#define CAC_NUM_CERTS_AND_KEYS 10
+
+static const char * cac_get_name(int type)
+{
+ switch (type) {
+ case SC_CARD_TYPE_CAC_I: return ("CAC I");
+ case SC_CARD_TYPE_CAC_II: return ("CAC II");
+ default: break;
+ }
+ return ("CAC");
+}
+
+/*
+ * These could move to a helper file for other cards that wish to use usage as a way of getting flags
+ */
+
+/* Only certain usages are valid for a given algorithm, return all the usages that the algorithm supports so we
+ * can use it as a filter for all the public and private key usages */
+static unsigned int
+cac_alg_flags_from_algorithm(int algorithm)
+{
+ switch (algorithm) {
+ case SC_ALGORITHM_RSA:
+ return SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_WRAP |
+ SC_PKCS15_PRKEY_USAGE_VERIFY | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER |
+ SC_PKCS15_PRKEY_USAGE_DECRYPT | SC_PKCS15_PRKEY_USAGE_UNWRAP |
+ SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER |
+ SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ case SC_ALGORITHM_DSA:
+ return SC_PKCS15_PRKEY_USAGE_VERIFY| SC_PKCS15_PRKEY_USAGE_SIGN |
+ SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+#ifdef SC_ALGORITHM_DH
+ case SC_ALGORITHM_DH:
+ return SC_PKCS15_PRKEY_USAGE_DERIVE ;
+#endif
+ case SC_ALGORITHM_EC:
+ return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+ SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ case SC_ALGORITHM_GOSTR3410:
+ return SC_PKCS15_PRKEY_USAGE_DERIVE | SC_PKCS15_PRKEY_USAGE_VERIFY|
+ SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ }
+ return 0;
+}
+
+
+/* These are the cert key usage bits that map to various PKCS #11 (and thus PKCS #15) flags */
+#define CAC_X509_USAGE_SIGNATURE \
+ (SC_PKCS15INIT_X509_DIGITAL_SIGNATURE | \
+ SC_PKCS15INIT_X509_NON_REPUDIATION | \
+ SC_PKCS15INIT_X509_KEY_CERT_SIGN | \
+ SC_PKCS15INIT_X509_CRL_SIGN)
+#define CAC_X509_USAGE_DERIVE \
+ SC_PKCS15INIT_X509_KEY_AGREEMENT
+#define CAC_X509_USAGE_UNWRAP \
+ (SC_PKCS15INIT_X509_KEY_ENCIPHERMENT | \
+ SC_PKCS15INIT_X509_KEY_AGREEMENT)
+#define CAC_X509_USAGE_DECRYPT \
+ (SC_PKCS15INIT_X509_DATA_ENCIPHERMENT \
+ /* | encipher? */)
+#define CAC_X509_USAGE_NONREPUDIATION \
+ SC_PKCS15INIT_X509_NON_REPUDIATION
+
+/* map a cert usage and algorithm to public and private key usages */
+static int
+cac_map_usage(unsigned long long cert_usage, int algorithm, unsigned int *pub_usage_ptr, unsigned int *pr_usage_ptr, int allow_nonrepudiation)
+{
+ unsigned int pub_usage = 0, pr_usage = 0;
+ unsigned int alg_flags = cac_alg_flags_from_algorithm(algorithm);
+
+ if (cert_usage & CAC_X509_USAGE_SIGNATURE) {
+ pub_usage |= SC_PKCS15_PRKEY_USAGE_VERIFY|SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER;
+ pr_usage |= SC_PKCS15_PRKEY_USAGE_SIGN|SC_PKCS15_PRKEY_USAGE_SIGNRECOVER;
+ }
+ if (cert_usage & CAC_X509_USAGE_DERIVE) {
+ pub_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+ pr_usage |= SC_PKCS15_PRKEY_USAGE_DERIVE;
+ }
+ if (cert_usage & (CAC_X509_USAGE_DECRYPT|CAC_X509_USAGE_UNWRAP)) {
+ pub_usage |= SC_PKCS15_PRKEY_USAGE_ENCRYPT;
+ pr_usage |= SC_PKCS15_PRKEY_USAGE_DECRYPT;
+ }
+ if (allow_nonrepudiation && (cert_usage & CAC_X509_USAGE_NONREPUDIATION)) {
+ pub_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ pr_usage |= SC_PKCS15_PRKEY_USAGE_NONREPUDIATION;
+ }
+ /* filter usages algorithm */
+ if (pub_usage_ptr) {
+ *pub_usage_ptr = pub_usage & alg_flags;
+ }
+ if (pr_usage_ptr) {
+ *pr_usage_ptr = pr_usage & alg_flags;
+ }
+ return SC_SUCCESS;
+}
+
+static int sc_pkcs15emu_cac_init(sc_pkcs15_card_t *p15card)
+{
+ static const pindata pins[] = {
+ { "1", NULL, "", 0x00,
+ SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
+ 8, 4, 8,
+ SC_PKCS15_PIN_FLAG_NEEDS_PADDING |
+ SC_PKCS15_PIN_FLAG_INITIALIZED ,
+ -1, 0xFF,
+ SC_PKCS15_CO_FLAG_PRIVATE },
+ { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+ };
+ /* oid for key usage */
+ static const struct sc_object_id usage_type = {{ 2, 5, 29, 15, -1 }};
+ unsigned long long usage;
+
+
+ /*
+ * The size of the key or the algid is not really known
+ * but can be derived from the certificates.
+ * the cert, pubkey and privkey are a set.
+ * Key usages bits taken from certificate key usage extension.
+ */
+
+ int r, i;
+ sc_card_t *card = p15card->card;
+ sc_serial_number_t serial;
+ char buf[SC_MAX_SERIALNR * 2 + 1];
+ int count;
+ char *token_name = NULL;
+
+
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+
+ memset(&serial, 0, sizeof(serial));
+
+ /* could read this off card if needed */
+
+ p15card->tokeninfo->label = strdup(cac_get_name(card->type));
+ p15card->tokeninfo->manufacturer_id = strdup(MANU_ID);
+
+ /*
+ * get serial number
+ */
+ r = sc_card_ctl(card, SC_CARDCTL_GET_SERIALNR, &serial);
+ if (r < 0) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"sc_card_ctl rc=%d",r);
+ p15card->tokeninfo->serial_number = strdup("00000000");
+ } else {
+ sc_bin_to_hex(serial.value, serial.len, buf, sizeof(buf), 0);
+ p15card->tokeninfo->serial_number = strdup(buf);
+ }
+
+ /* set pins */
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding pins...");
+ for (i = 0; pins[i].id; i++) {
+ struct sc_pkcs15_auth_info pin_info;
+ struct sc_pkcs15_object pin_obj;
+ const char * label;
+
+ memset(&pin_info, 0, sizeof(pin_info));
+ memset(&pin_obj, 0, sizeof(pin_obj));
+
+ pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
+ sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
+ pin_info.attrs.pin.reference = pins[i].ref;
+ pin_info.attrs.pin.flags = pins[i].flags;
+ pin_info.attrs.pin.type = pins[i].type;
+ pin_info.attrs.pin.min_length = pins[i].minlen;
+ pin_info.attrs.pin.stored_length = pins[i].storedlen;
+ pin_info.attrs.pin.max_length = pins[i].maxlen;
+ pin_info.attrs.pin.pad_char = pins[i].pad_char;
+ sc_format_path(pins[i].path, &pin_info.path);
+ pin_info.tries_left = -1;
+
+ label = pins[i].label? pins[i].label : cac_get_name(card->type);
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC Adding pin %d label=%s",i, label);
+ strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1);
+ pin_obj.flags = pins[i].obj_flags;
+
+ r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
+ if (r < 0)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+ }
+
+ /* set other objects */
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_GENERIC_OBJECTS, &count);
+ for (i = 0; i < count; i++) {
+ struct sc_pkcs15_data_info obj_info;
+ struct sc_pkcs15_object obj_obj;
+
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_GENERIC_OBJECT, &obj_info);
+ if (r < 0)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+ memset(&obj_obj, 0, sizeof(obj_obj));
+ memcpy(obj_obj.label, obj_info.app_label, sizeof(obj_obj.label));
+
+ r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT,
+ &obj_obj, &obj_info);
+ if (r < 0)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+ }
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_GENERIC_OBJECTS, &count);
+
+ /*
+ * certs, pubkeys and priv keys are related and we assume
+ * they are in order
+ * We need to read the cert, get modulus and keylen
+ * We use those for the pubkey, and priv key objects.
+ */
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "CAC adding certs, pub and priv keys...");
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_INIT_GET_CERT_OBJECTS, &count);
+ for (i = 0; i < count; i++) {
+ struct sc_pkcs15_data_info obj_info;
+ struct sc_pkcs15_cert_info cert_info;
+ struct sc_pkcs15_pubkey_info pubkey_info;
+ struct sc_pkcs15_prkey_info prkey_info;
+ struct sc_pkcs15_object cert_obj;
+ struct sc_pkcs15_object pubkey_obj;
+ struct sc_pkcs15_object prkey_obj;
+ sc_pkcs15_der_t cert_der;
+ sc_pkcs15_cert_t *cert_out;
+
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_GET_NEXT_CERT_OBJECT, &obj_info);
+
+ memset(&cert_info, 0, sizeof(cert_info));
+ memset(&pubkey_info, 0, sizeof(pubkey_info));
+ memset(&prkey_info, 0, sizeof(prkey_info));
+ memset(&cert_obj, 0, sizeof(cert_obj));
+ memset(&pubkey_obj, 0, sizeof(pubkey_obj));
+ memset(&prkey_obj, 0, sizeof(prkey_obj));
+
+ cert_info.id = obj_info.id;
+ pubkey_info.id = obj_info.id;
+ prkey_info.id = obj_info.id;
+ cert_info.path = obj_info.path;
+ prkey_info.path = obj_info.path;
+ /* Add 0x3f00 to the front of prkey_info.path to make sc_key_file happy */
+ /* only do this if our path.len is 1 or 2 */
+ if (prkey_info.path.len && prkey_info.path.len <= 2) {
+ prkey_info.path.value[2] = prkey_info.path.value[0];
+ prkey_info.path.value[3] = prkey_info.path.value[1];
+ prkey_info.path.value[0] = 0x3f;
+ prkey_info.path.value[1] = 0x00;
+ prkey_info.path.len += 2;
+ }
+ pubkey_info.native = 1;
+ pubkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1];
+ prkey_info.key_reference = ((int)obj_info.id.value[0]) << 8 || obj_info.id.value[1];
+ prkey_info.native = 1;
+
+ memcpy(cert_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+ memcpy(pubkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+ memcpy(prkey_obj.label, obj_info.app_label, sizeof(obj_info.app_label));
+ prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE;
+ sc_pkcs15_format_id(pins[0].id, &prkey_obj.auth_id);
+
+ r = sc_pkcs15_read_file(p15card, &cert_info.path, &cert_der.value, &cert_der.len);
+
+ if (r) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "No cert found,i=%d", i);
+ continue;
+ }
+ cert_info.path.count = cert_der.len;
+
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cert len=%d, cert_info.path.count=%d r=%d\n",
+ cert_der.len, cert_info.path.count, r);
+ sc_debug_hex(card->ctx, SC_LOG_DEBUG_NORMAL, "cert", cert_der.value, cert_der.len);
+
+ /* cache it using the PKCS15 emulation objects */
+ /* as it does not change */
+ if (cert_der.value) {
+ cert_info.value.value = cert_der.value;
+ cert_info.value.len = cert_der.len;
+ cert_info.path.len = 0; /* use in mem cert from now on */
+ }
+
+ /* following will find the cached cert in cert_info */
+ r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
+ if (r < 0 || cert_out->key == NULL) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Failed to read/parse the certificate r=%d",r);
+ continue;
+ }
+
+ r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info);
+ if (r < 0) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, " Failed to add cert obj r=%d",r);
+ continue;
+ }
+ /* set the token name to the name of the CN of the first certificate */
+ if (!token_name) {
+ u8 * cn_name = NULL;
+ size_t cn_len = 0;
+ static const struct sc_object_id cn_oid = {{ 2, 5, 4, 3, -1 }};
+ r = sc_pkcs15_get_name_from_dn(card->ctx, cert_out->subject,
+ cert_out->subject_len, &cn_oid, &cn_name, &cn_len);
+ if (r == SC_SUCCESS) {
+ token_name = malloc (cn_len+1);
+ memcpy(token_name, cn_name, cn_len);
+ free(cn_name);
+ token_name[cn_len] = 0;
+ free(p15card->tokeninfo->label);
+ p15card->tokeninfo->label = token_name;
+ }
+ }
+
+
+ r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, cert_out->key, &pubkey_info.direct.spki.value, &pubkey_info.direct.spki.len);
+ if (r < 0)
+ goto fail;
+ pubkey_obj.emulated = cert_out->key;
+
+ r = sc_pkcs15_get_bitstring_extension(card->ctx, cert_out, &usage_type, &usage, NULL);
+ if (r < 0) {
+ usage = 0xd9ULL; /* basic default usage */
+ }
+ cac_map_usage(usage, cert_out->key->algorithm, &pubkey_info.usage, &prkey_info.usage, 1);
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "cert %s: cert_usage=0x%llx, pub_usage=0x%x priv_usage=0x%x\n",
+ sc_dump_hex(cert_info.id.value, cert_info.id.len),
+ usage, pubkey_info.usage, prkey_info.usage);
+ if (cert_out->key->algorithm != SC_ALGORITHM_RSA) {
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"unsupported key.algorithm %d", cert_out->key->algorithm);
+ continue;
+ } else {
+ pubkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+ prkey_info.modulus_length = cert_out->key->u.rsa.modulus.len * 8;
+ r = sc_pkcs15emu_add_rsa_pubkey(p15card, &pubkey_obj, &pubkey_info);
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa public key r=%d usage=%x",r, pubkey_info.usage);
+ if (r < 0)
+ goto fail;
+ r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info);
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "adding rsa private key r=%d usage=%x",r, prkey_info.usage);
+ }
+
+ cert_out->key = NULL;
+fail:
+ sc_pkcs15_free_certificate(cert_out);
+ if (r < 0)
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r); /* should not fail */
+
+ }
+ r = (card->ops->card_ctl)(card, SC_CARDCTL_CAC_FINAL_GET_CERT_OBJECTS, &count);
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card,
+ struct sc_aid *aid, sc_pkcs15emu_opt_t *opts)
+{
+ sc_card_t *card = p15card->card;
+ sc_context_t *ctx = card->ctx;
+
+ LOG_FUNC_CALLED(ctx);
+
+ if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)
+ return sc_pkcs15emu_cac_init(p15card);
+ else {
+ int r = cac_detect_card(p15card);
+ if (r)
+ return SC_ERROR_WRONG_CARD;
+ return sc_pkcs15emu_cac_init(p15card);
+ }
+}
diff --git a/src/libopensc/pkcs15-cert.c b/src/libopensc/pkcs15-cert.c
index bcc0e5a..440f531 100644
--- a/src/libopensc/pkcs15-cert.c
+++ b/src/libopensc/pkcs15-cert.c
@@ -264,7 +264,7 @@ sc_pkcs15_get_extension(struct sc_context *ctx, struct sc_pkcs15_cert *cert,
/*
* use the sc_asn1_decoder for clarity. NOTE it would be more efficient to do this by hand
- * so we avoid the man malloc/frees here, but one hopes that one day the asn1_decode will allow
+ * so we avoid the many malloc/frees here, but one hopes that one day the asn1_decode will allow
* a 'static pointer' flag that returns a const pointer to the actual asn1 space so we only need
* to make a final copy of the extension value before we return */
critical = 0;
@@ -316,7 +316,7 @@ sc_pkcs15_get_bitstring_extension(struct sc_context *ctx,
u8 *bit_string = NULL;
size_t bit_string_len=0, val_len = sizeof(*value);
struct sc_asn1_entry asn1_bit_string[] = {
- { "bitString", SC_ASN1_BIT_STRING, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len },
+ { "bitString", SC_ASN1_BIT_STRING_NI, SC_ASN1_TAG_BIT_STRING, 0, value, &val_len },
{ NULL, 0, 0, 0, NULL, NULL }
};
@@ -325,6 +325,7 @@ sc_pkcs15_get_bitstring_extension(struct sc_context *ctx,
r = sc_asn1_decode(ctx, asn1_bit_string, bit_string, bit_string_len, NULL, NULL);
LOG_TEST_RET(ctx, r, "Decoding extension bit string");
+ free(bit_string);
LOG_FUNC_RETURN(ctx, SC_SUCCESS);
}
diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c
index 0163f48..0fdcbed 100644
--- a/src/libopensc/pkcs15-syn.c
+++ b/src/libopensc/pkcs15-syn.c
@@ -44,6 +44,7 @@ struct sc_pkcs15_emulator_handler builtin_emulators[] = {
{ "itacns", sc_pkcs15emu_itacns_init_ex },
{ "postecert", sc_pkcs15emu_postecert_init_ex },
{ "PIV-II", sc_pkcs15emu_piv_init_ex },
+ { "cac", sc_pkcs15emu_cac_init_ex },
{ "gemsafeGPK", sc_pkcs15emu_gemsafeGPK_init_ex },
{ "gemsafeV1", sc_pkcs15emu_gemsafeV1_init_ex },
{ "actalis", sc_pkcs15emu_actalis_init_ex },
diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h
index 5e3ae59..22db061 100644
--- a/src/libopensc/pkcs15-syn.h
+++ b/src/libopensc/pkcs15-syn.h
@@ -37,6 +37,7 @@ int sc_pkcs15emu_tcos_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_
int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *);
int sc_pkcs15emu_postecert_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *);
int sc_pkcs15emu_piv_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts);
+int sc_pkcs15emu_cac_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts);
int sc_pkcs15emu_gemsafeGPK_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts);
int sc_pkcs15emu_gemsafeV1_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts);
int sc_pkcs15emu_actalis_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *, sc_pkcs15emu_opt_t *opts);
diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c
index 626c522..306ec57 100644
--- a/src/libopensc/sc.c
+++ b/src/libopensc/sc.c
@@ -178,6 +178,13 @@ unsigned short bebytes2ushort(const u8 *buf)
return (unsigned short) (buf[0] << 8 | buf[1]);
}
+unsigned short lebytes2ushort(const u8 *buf)
+{
+ if (buf == NULL)
+ return 0U;
+ return (unsigned short)buf[1] << 8 | (unsigned short)buf[0];
+}
+
void sc_init_oid(struct sc_object_id *oid)
{
int ii;
diff --git a/src/libopensc/simpletlv.c b/src/libopensc/simpletlv.c
new file mode 100644
index 0000000..f526a1c
--- /dev/null
+++ b/src/libopensc/simpletlv.c
@@ -0,0 +1,99 @@
+/*
+ * simpletlv.c: Simple TLV encoding and decoding functions
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <assert.h>
+#include <stdlib.h>
+
+#include "internal.h"
+#include "simpletlv.h"
+
+/*
+ * Put a tag/length record to a file in Simple TLV based on the datalen
+ * content length.
+ */
+int
+sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr)
+{
+ u8 *p = out;
+
+ if (outlen < 2 || (outlen < 4 && datalen >= 0xff))
+ return SC_ERROR_INVALID_ARGUMENTS;
+
+ /* tag is just number between 0x01 and 0xFE */
+ if (tag == 0x00 || tag == 0xff)
+ return SC_ERROR_INVALID_ARGUMENTS;
+
+ *p++ = tag; /* tag is single byte */
+ if (datalen < 0xff) {
+ /* short value up to 255 */
+ *p++ = (u8)datalen; /* is in the second byte */
+ } else if (datalen < 0xffff) {
+ /* longer values up to 65535 */
+ *p++ = (u8)0xff; /* first byte is 0xff */
+ *p++ = (u8)datalen & 0xff;
+ *p++ = (u8)(datalen >> 8) & 0xff; /* LE */
+ } else {
+ /* we can't store more than two bytes in Simple TLV */
+ return SC_ERROR_WRONG_LENGTH;
+ }
+ if (ptr != NULL)
+ *ptr = p;
+ return SC_SUCCESS;
+}
+
+/* Read the TL file and return appropriate tag and the length of associated
+ * content.
+ */
+int
+sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen)
+{
+ size_t len;
+ u8 *p = *buf;
+
+ if (buflen < 2) {
+ *buf = p+buflen;
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+
+ *tag_out = *p++;
+ len = *p++;
+ if (len == 0xff) {
+ /* don't crash on bad data */
+ if (buflen < 4) {
+ *taglen = 0;
+ return SC_ERROR_INVALID_ARGUMENTS;
+ }
+ len = lebytes2ushort(p);
+ p++;
+ }
+ *taglen = len;
+ *buf = p;
+ return SC_SUCCESS;
+}
diff --git a/src/libopensc/simpletlv.h b/src/libopensc/simpletlv.h
new file mode 100644
index 0000000..c207441
--- /dev/null
+++ b/src/libopensc/simpletlv.h
@@ -0,0 +1,54 @@
+/*
+ * simpletlv.h: Simple TLV header file
+ *
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Authors: Robert Relyea <rrelyea at redhat.com>
+ * Jakub Jelen <jjelen at redhat.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef _OPENSC_SIMPLETLV_H
+#define _OPENSC_SIMPLETLV_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "libopensc/opensc.h"
+#include "libopensc/pkcs15.h"
+
+/*
+ * Create a tag/length file in Simple TLV based on the val_len content length
+ * @param tag Tag to store into the TL file
+ * @param datalen Data length to store into the TL file
+ * @param out TL byte array to write into
+ * @param outlen The length of the output array
+ * @param ptr The end of the TL record written
+ * @return SC_SUCCESS for correct input
+ */
+int sc_simpletlv_put_tag(u8 tag, size_t datalen, u8 *out, size_t outlen, u8 **ptr);
+
+/* get the Simple TLV tag and length.
+ * @param buf Pointer to the TL file
+ * @param buflen The length of TL file
+ * @param tag_out The tag from the TL file
+ * @param taglen The length of the V record
+ * @return SC_SUCCESS on valid input
+ */
+int sc_simpletlv_read_tag(u8 **buf, size_t buflen, u8 *tag_out, size_t *taglen);
+
+#endif
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-opensc/opensc.git
More information about the pkg-opensc-commit
mailing list