commit a2efcb184fcb0302bfadeb97b5cf8953cb77d0fb
Author: Jakub Jelen <jjelen@redhat.com>
Date: Tue Oct 9 16:03:19 2018 +0200
WIP: Add minimal CAC1 driver for legacy cards.
It is using the same pkcs15 backend as the CAC2 cards as well as some of
the CAC2 driver methods.
The separation is made mostly for easier card matching.
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
index a99b0c57..5a17df2b 100644
--- a/src/libopensc/Makefile.am
+++ b/src/libopensc/Makefile.am
@@ -40,7 +40,8 @@ libopensc_la_SOURCES_BASE = \
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-cac.c card-muscle.c card-acos5.c \
+ card-piv.c card-cac-common.c card-cac.c card-cac1.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 \
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
index 10a3b0a9..7ef642b6 100644
--- a/src/libopensc/Makefile.mak
+++ b/src/libopensc/Makefile.mak
@@ -19,7 +19,7 @@ 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-cac.obj card-piv.obj card-muscle.obj \
+ card-incrypto34.obj card-cac.obj card-cac1.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 \
diff --git a/src/libopensc/card-cac-common.c b/src/libopensc/card-cac-common.c
new file mode 100644
index 00000000..6c338bf9
--- /dev/null
+++ b/src/libopensc/card-cac-common.c
@@ -0,0 +1,116 @@
+/*
+ * card-cac-common.c: Code shared among CAC1 and CAC2 drivers
+ *
+ * Copyright (C) 2018, Red Hat, Inc.
+ *
+ * Author: 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 <string.h>
+#include <stdlib.h>
+
+#ifdef _WIN32
+#include <io.h>
+#else
+#include <unistd.h>
+#endif
+
+#include "internal.h"
+#include "iso7816.h"
+#include "card-cac-common.h"
+
+/* default certificate labels for the CAC card */
+const char *cac_labels[MAX_CAC_SLOTS] = {
+ "CAC ID Certificate",
+ "CAC Email Signature Certificate",
+ "CAC Email Encryption Certificate",
+ "CAC Cert 4",
+ "CAC Cert 5",
+ "CAC Cert 6",
+ "CAC Cert 7",
+ "CAC Cert 8",
+ "CAC Cert 9",
+ "CAC Cert 10",
+ "CAC Cert 11",
+ "CAC Cert 12",
+ "CAC Cert 13",
+ "CAC Cert 14",
+ "CAC Cert 15",
+ "CAC Cert 16"
+};
+
+const char *get_cac_label(int index)
+{
+ if (index < 0 || index >= MAX_CAC_SLOTS)
+ return NULL;
+
+ return cac_labels[index];
+}
+
+static 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 */
+static size_t cac_list_meter(const void *el) {
+ return sizeof(cac_object_t);
+}
+
+cac_private_data_t *cac_new_private_data(void)
+{
+ cac_private_data_t *priv;
+ priv = calloc(1, sizeof(cac_private_data_t));
+ if (!priv)
+ return NULL;
+ 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;
+}
+
+void cac_free_private_data(cac_private_data_t *priv)
+{
+ free(priv->cac_id);
+ free(priv->cache_buf);
+ free(priv->aca_path);
+ list_destroy(&priv->pki_list);
+ list_destroy(&priv->general_list);
+ free(priv);
+ return;
+}
+
+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;
+}
+
diff --git a/src/libopensc/card-cac-common.h b/src/libopensc/card-cac-common.h
new file mode 100644
index 00000000..77f14761
--- /dev/null
+++ b/src/libopensc/card-cac-common.h
@@ -0,0 +1,89 @@
+/*
+ * card-cac-common.h: Code shared among CAC1 and CAC2 drivers
+ *
+ * Copyright (C) 2018, Red Hat, Inc.
+ *
+ * Author: 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
+ */
+
+#ifndef HAVE_CARD_CAC_COMMON_H
+#define HAVE_CARD_CAC_COMMON_H
+
+#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
+
+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;
+
+/*
+ * 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 */
+ sc_path_t *aca_path; /* ACA path to be selected before pin verification */
+} cac_private_data_t;
+
+#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
+
+/*
+ * Set up the normal CAC paths
+ */
+#define CAC_1_RID "\xA0\x00\x00\x00\x79"
+#define CAC_TO_AID(x) x, sizeof(x)-1
+
+
+#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */
+
+/* template for a CAC pki object */
+static const cac_object_t cac_cac_pki_obj = {
+ "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
+ { CAC_TO_AID(CAC_1_RID "\x01\x00") } }
+};
+
+/* template for emulated cuid */
+static const cac_cuid_t cac_cac_cuid = {
+ { 0xa0, 0x00, 0x00, 0x00, 0x79 },
+ 2, 2, 0
+};
+
+cac_private_data_t *cac_new_private_data(void);
+void cac_free_private_data(cac_private_data_t *priv);
+int cac_add_object_to_list(list_t *list, const cac_object_t *object);
+const char *get_cac_label(int index);
+
+#endif /* HAVE_CARD_CAC_COMMON_H */
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
index bd4e0336..f9620009 100644
--- a/src/libopensc/card-cac.c
+++ b/src/libopensc/card-cac.c
@@ -58,8 +58,8 @@
#include "compression.h"
#endif
#include "iso7816.h"
+#include "card-cac-common.h"
-#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
/*
* CAC hardware and APDU constants
*/
@@ -147,20 +147,6 @@ typedef struct cac_card_url {
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;
-
#define CAC_MAX_OBJECTS 16
typedef struct {
@@ -190,82 +176,10 @@ typedef struct {
#define CAC_OBJECT_TYPE_TLV_FILE 4
#define CAC_OBJECT_TYPE_GENERIC 5
-/*
- * 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 */
- sc_path_t *aca_path; /* ACA path to be selected before pin verification */
-} cac_private_data_t;
-
-#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
-
-static 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 */
-static 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));
- if (!priv)
- return NULL;
- 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);
- free(priv->aca_path);
- 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"
static const sc_path_t cac_ACA_Path = {
"", 0,
@@ -279,39 +193,6 @@ static const sc_path_t cac_CCC_Path = {
{ CAC_TO_AID(CAC_2_RID "\xDB\x00") }
};
-#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */
-/* 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 4",
- "CAC Cert 5",
- "CAC Cert 6",
- "CAC Cert 7",
- "CAC Cert 8",
- "CAC Cert 9",
- "CAC Cert 10",
- "CAC Cert 11",
- "CAC Cert 12",
- "CAC Cert 13",
- "CAC Cert 14",
- "CAC Cert 15",
- "CAC Cert 16"
-};
-
-/* template for a CAC pki object */
-static const cac_object_t cac_cac_pki_obj = {
- "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
- { CAC_TO_AID(CAC_1_RID "\x01\x00") } }
-};
-
-/* template for emulated cuid */
-static const cac_cuid_t cac_cac_cuid = {
- { 0xa0, 0x00, 0x00, 0x00, 0x79 },
- 2, 2, 0
-};
-
/*
* 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.
@@ -1178,7 +1059,7 @@ static int cac_get_properties(sc_card_t *card, cac_properties_t *prop)
*
* 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)
+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;
@@ -1356,7 +1237,7 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc
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);
+ return cac_select_file_by_type(card, in_path, file_out);
}
static int cac_finish(sc_card_t *card)
@@ -1374,13 +1255,13 @@ static int cac_finish(sc_card_t *card)
/* 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);
+ 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, SC_CARD_TYPE_CAC_II);
+ 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)
@@ -1432,7 +1313,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
/* Call without OID set will just select the AID without subseqent
* OID selection, which we need to figure out just now
*/
- cac_select_file_by_type(card, &new_object.path, NULL, SC_CARD_TYPE_CAC_II);
+ cac_select_file_by_type(card, &new_object.path, NULL);
r = cac_get_properties(card, &prop);
if (r < 0)
return SC_ERROR_INTERNAL;
@@ -1444,7 +1325,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
"ACA: pki_object found, cert_next=%d (%s), privkey=%d",
- priv->cert_next, cac_labels[priv->cert_next],
+ priv->cert_next, get_cac_label(priv->cert_next),
prop.objects[i].privatekey);
/* If the private key is not initialized, we can safely
@@ -1460,7 +1341,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
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 = cac_labels[priv->cert_next];
+ 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++;
@@ -1488,7 +1369,7 @@ static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card
*/
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.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);
@@ -1629,7 +1510,7 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl,
if (r < 0)
return r;
- r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II);
+ r = cac_select_file_by_type(card, &new_path, NULL);
if (r < 0)
return r;
@@ -1740,7 +1621,7 @@ 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, SC_CARD_TYPE_CAC_II);
+ return cac_select_file_by_type(card, &applet_path, NULL);
}
/*
@@ -1785,7 +1666,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *
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];
+ 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;
@@ -1796,8 +1677,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *
/* populate non-PKI objects */
for (i=0; i < cac_object_count; i++) {
- r = cac_select_file_by_type(card, &cac_objects[i].path, NULL,
- SC_CARD_TYPE_CAC_II);
+ 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),",
diff --git a/src/libopensc/card-cac1.c b/src/libopensc/card-cac1.c
new file mode 100644
index 00000000..f3a16547
--- /dev/null
+++ b/src/libopensc/card-cac1.c
@@ -0,0 +1,563 @@
+/*
+ * card-cac1.c: Support for legacy CAC-1
+ * 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
+ /* 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"
+#include "card-cac-common.h"
+
+/*
+ * CAC hardware and APDU constants
+ */
+#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */
+
+/*
+ * 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 = SC_SUCCESS;
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
+ /* 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;
+ }
+ if (apdu.resplen == 0) {
+ r = SC_ERROR_INTERNAL;
+ 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) {
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
+ }
+ r = size - left;
+ if (*out_buf == NULL) {
+ *out_buf = malloc(r);
+ if (*out_buf == NULL) {
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,
+ SC_ERROR_OUT_OF_MEMORY);
+ }
+ memcpy(*out_buf, buf, r);
+ }
+ *out_len = r;
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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 *val = NULL;
+ u8 *cert_ptr;
+ size_t val_len;
+ size_t len, cert_len;
+ u8 cert_type;
+
+ 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=%"SC_FORMAT_LEN_SIZE_T"u",
+ 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=%"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;
+ }
+
+ r = cac_cac1_get_certificate(card, &val, &val_len);
+ if (r < 0)
+ goto done;
+
+ cert_type = val[0];
+ cert_ptr = val + 1;
+ cert_len = val_len - 1;
+
+ /* 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;
+ cert_ptr = val;
+ } 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);
+ }
+
+ /* 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 (val)
+ free(val);
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
+}
+
+/*
+ * 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->aid=%x %x %x %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u, path->value = %x %x %x %x len=%"SC_FORMAT_LEN_SIZE_T"u 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=%p index=%d count=%d\n",
+ 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 */
+ /* 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;
+ apdu.p2 = 0x00;
+
+ 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);
+
+ /* 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;
+ 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);
+}
+
+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 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) {
+ u8 data[2];
+ sc_apdu_t apdu;
+
+ /* Try to read first two bytes of the buffer to
+ * make sure it is not just malfunctioning card
+ */
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2,
+ CAC_INS_GET_CERTIFICATE, 0x00, 0x00);
+ apdu.le = 0x02;
+ apdu.resplen = 2;
+ apdu.resp = data;
+ r = sc_transmit_apdu(card, &apdu);
+ /* SW1 = 0x63 means more data in CAC1 */
+ if (r == SC_SUCCESS && apdu.sw1 != 0x63)
+ continue;
+
+ *index_out = i;
+ return SC_SUCCESS;
+ }
+ }
+ return SC_ERROR_OBJECT_NOT_FOUND;
+}
+
+static int cac_populate_cac1(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;
+ 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 = 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);
+ }
+ }
+
+ /*
+ * 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;
+ 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;
+ 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;
+}
+
+/*
+ * 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 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-1");
+ 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_cac1(card, index, priv);
+ if (r == SC_SUCCESS) {
+ card->type = SC_CARD_TYPE_CAC_I;
+ 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) {
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, 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;
+
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
+}
+
+static struct sc_card_operations cac_ops;
+
+static struct sc_card_driver cac1_drv = {
+ "Common Access Card (CAC 1)",
+ "cac1",
+ &cac_ops,
+ NULL, 0, NULL
+};
+
+static struct sc_card_driver * sc_get_driver(void)
+{
+ /* Inherit most of the things from the CAC driver */
+ struct sc_card_driver *cac_drv = sc_get_cac_driver();
+
+ cac_ops = *cac_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.read_binary = cac_read_binary;
+
+ return &cac1_drv;
+}
+
+
+struct sc_card_driver * sc_get_cac1_driver(void)
+{
+ return sc_get_driver();
+}
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
index 5d545b35..f4df17fb 100644
--- a/src/libopensc/cards.h
+++ b/src/libopensc/cards.h
@@ -286,6 +286,7 @@ 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);
+extern sc_card_driver_t *sc_get_cac1_driver(void);
extern sc_card_driver_t *sc_get_npa_driver(void);
#ifdef __cplusplus
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
index 98e6038a..5b5d4996 100644
--- a/src/libopensc/ctx.c
+++ b/src/libopensc/ctx.c
@@ -146,6 +146,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
{ "openpgp", (void *(*)(void)) sc_get_openpgp_driver },
{ "jpki", (void *(*)(void)) sc_get_jpki_driver },
{ "npa", (void *(*)(void)) sc_get_npa_driver },
+ { "cac1", (void *(*)(void)) sc_get_cac1_driver },
/* The default driver should be last, as it handles all the
* unrecognized cards. */
{ "default", (void *(*)(void)) sc_get_default_driver },