|
|
b7ace2 |
commit a2efcb184fcb0302bfadeb97b5cf8953cb77d0fb
|
|
|
b7ace2 |
Author: Jakub Jelen <jjelen@redhat.com>
|
|
|
b7ace2 |
Date: Tue Oct 9 16:03:19 2018 +0200
|
|
|
b7ace2 |
|
|
|
b7ace2 |
WIP: Add minimal CAC1 driver for legacy cards.
|
|
|
b7ace2 |
|
|
|
b7ace2 |
It is using the same pkcs15 backend as the CAC2 cards as well as some of
|
|
|
b7ace2 |
the CAC2 driver methods.
|
|
|
b7ace2 |
|
|
|
b7ace2 |
The separation is made mostly for easier card matching.
|
|
|
b7ace2 |
|
|
|
b7ace2 |
diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am
|
|
|
b7ace2 |
index a99b0c57..5a17df2b 100644
|
|
|
b7ace2 |
--- a/src/libopensc/Makefile.am
|
|
|
b7ace2 |
+++ b/src/libopensc/Makefile.am
|
|
|
b7ace2 |
@@ -40,7 +40,8 @@ libopensc_la_SOURCES_BASE = \
|
|
|
b7ace2 |
card-mcrd.c card-starcos.c card-openpgp.c card-jcop.c \
|
|
|
b7ace2 |
card-oberthur.c card-belpic.c card-atrust-acos.c \
|
|
|
b7ace2 |
card-entersafe.c card-epass2003.c card-coolkey.c card-incrypto34.c \
|
|
|
b7ace2 |
- card-piv.c card-cac.c card-muscle.c card-acos5.c \
|
|
|
b7ace2 |
+ card-piv.c card-cac-common.c card-cac.c card-cac1.c \
|
|
|
b7ace2 |
+ card-muscle.c card-acos5.c \
|
|
|
b7ace2 |
card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \
|
|
|
b7ace2 |
card-rtecp.c card-westcos.c card-myeid.c \
|
|
|
b7ace2 |
card-itacns.c card-authentic.c \
|
|
|
b7ace2 |
diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak
|
|
|
b7ace2 |
index 10a3b0a9..7ef642b6 100644
|
|
|
b7ace2 |
--- a/src/libopensc/Makefile.mak
|
|
|
b7ace2 |
+++ b/src/libopensc/Makefile.mak
|
|
|
b7ace2 |
@@ -19,7 +19,7 @@ OBJECTS = \
|
|
|
b7ace2 |
card-mcrd.obj card-starcos.obj card-openpgp.obj card-jcop.obj \
|
|
|
b7ace2 |
card-oberthur.obj card-belpic.obj card-atrust-acos.obj \
|
|
|
b7ace2 |
card-entersafe.obj card-epass2003.obj card-coolkey.obj \
|
|
|
b7ace2 |
- card-incrypto34.obj card-cac.obj card-piv.obj card-muscle.obj \
|
|
|
b7ace2 |
+ card-incrypto34.obj card-cac.obj card-cac1.obj card-piv.obj card-muscle.obj \
|
|
|
b7ace2 |
card-acos5.obj \
|
|
|
b7ace2 |
card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \
|
|
|
b7ace2 |
card-rtecp.obj card-westcos.obj card-myeid.obj \
|
|
|
b7ace2 |
diff --git a/src/libopensc/card-cac-common.c b/src/libopensc/card-cac-common.c
|
|
|
b7ace2 |
new file mode 100644
|
|
|
b7ace2 |
index 00000000..6c338bf9
|
|
|
b7ace2 |
--- /dev/null
|
|
|
b7ace2 |
+++ b/src/libopensc/card-cac-common.c
|
|
|
b7ace2 |
@@ -0,0 +1,116 @@
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * card-cac-common.c: Code shared among CAC1 and CAC2 drivers
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * Copyright (C) 2018, Red Hat, Inc.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * Author: Jakub Jelen <jjelen@redhat.com>
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is free software; you can redistribute it and/or
|
|
|
b7ace2 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License as published by the Free Software Foundation; either
|
|
|
b7ace2 |
+ * version 2.1 of the License, or (at your option) any later version.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is distributed in the hope that it will be useful,
|
|
|
b7ace2 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
b7ace2 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
b7ace2 |
+ * Lesser General Public License for more details.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License along with this library; if not, write to the Free Software
|
|
|
b7ace2 |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#if HAVE_CONFIG_H
|
|
|
b7ace2 |
+#include "config.h"
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#include <ctype.h>
|
|
|
b7ace2 |
+#include <string.h>
|
|
|
b7ace2 |
+#include <stdlib.h>
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#ifdef _WIN32
|
|
|
b7ace2 |
+#include <io.h>
|
|
|
b7ace2 |
+#else
|
|
|
b7ace2 |
+#include <unistd.h>
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#include "internal.h"
|
|
|
b7ace2 |
+#include "iso7816.h"
|
|
|
b7ace2 |
+#include "card-cac-common.h"
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* default certificate labels for the CAC card */
|
|
|
b7ace2 |
+const char *cac_labels[MAX_CAC_SLOTS] = {
|
|
|
b7ace2 |
+ "CAC ID Certificate",
|
|
|
b7ace2 |
+ "CAC Email Signature Certificate",
|
|
|
b7ace2 |
+ "CAC Email Encryption Certificate",
|
|
|
b7ace2 |
+ "CAC Cert 4",
|
|
|
b7ace2 |
+ "CAC Cert 5",
|
|
|
b7ace2 |
+ "CAC Cert 6",
|
|
|
b7ace2 |
+ "CAC Cert 7",
|
|
|
b7ace2 |
+ "CAC Cert 8",
|
|
|
b7ace2 |
+ "CAC Cert 9",
|
|
|
b7ace2 |
+ "CAC Cert 10",
|
|
|
b7ace2 |
+ "CAC Cert 11",
|
|
|
b7ace2 |
+ "CAC Cert 12",
|
|
|
b7ace2 |
+ "CAC Cert 13",
|
|
|
b7ace2 |
+ "CAC Cert 14",
|
|
|
b7ace2 |
+ "CAC Cert 15",
|
|
|
b7ace2 |
+ "CAC Cert 16"
|
|
|
b7ace2 |
+};
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+const char *get_cac_label(int index)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ if (index < 0 || index >= MAX_CAC_SLOTS)
|
|
|
b7ace2 |
+ return NULL;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ return cac_labels[index];
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static int cac_list_compare_path(const void *a, const void *b)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ if (a == NULL || b == NULL)
|
|
|
b7ace2 |
+ return 1;
|
|
|
b7ace2 |
+ return memcmp( &((cac_object_t *) a)->path,
|
|
|
b7ace2 |
+ &((cac_object_t *) b)->path, sizeof(sc_path_t));
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* For SimCList autocopy, we need to know the size of the data elements */
|
|
|
b7ace2 |
+static size_t cac_list_meter(const void *el) {
|
|
|
b7ace2 |
+ return sizeof(cac_object_t);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+cac_private_data_t *cac_new_private_data(void)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ cac_private_data_t *priv;
|
|
|
b7ace2 |
+ priv = calloc(1, sizeof(cac_private_data_t));
|
|
|
b7ace2 |
+ if (!priv)
|
|
|
b7ace2 |
+ return NULL;
|
|
|
b7ace2 |
+ list_init(&priv->pki_list);
|
|
|
b7ace2 |
+ list_attributes_comparator(&priv->pki_list, cac_list_compare_path);
|
|
|
b7ace2 |
+ list_attributes_copy(&priv->pki_list, cac_list_meter, 1);
|
|
|
b7ace2 |
+ list_init(&priv->general_list);
|
|
|
b7ace2 |
+ list_attributes_comparator(&priv->general_list, cac_list_compare_path);
|
|
|
b7ace2 |
+ list_attributes_copy(&priv->general_list, cac_list_meter, 1);
|
|
|
b7ace2 |
+ /* set other fields as appropriate */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ return priv;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+void cac_free_private_data(cac_private_data_t *priv)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ free(priv->cac_id);
|
|
|
b7ace2 |
+ free(priv->cache_buf);
|
|
|
b7ace2 |
+ free(priv->aca_path);
|
|
|
b7ace2 |
+ list_destroy(&priv->pki_list);
|
|
|
b7ace2 |
+ list_destroy(&priv->general_list);
|
|
|
b7ace2 |
+ free(priv);
|
|
|
b7ace2 |
+ return;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+int cac_add_object_to_list(list_t *list, const cac_object_t *object)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ if (list_append(list, object) < 0)
|
|
|
b7ace2 |
+ return SC_ERROR_UNKNOWN;
|
|
|
b7ace2 |
+ return SC_SUCCESS;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
diff --git a/src/libopensc/card-cac-common.h b/src/libopensc/card-cac-common.h
|
|
|
b7ace2 |
new file mode 100644
|
|
|
b7ace2 |
index 00000000..77f14761
|
|
|
b7ace2 |
--- /dev/null
|
|
|
b7ace2 |
+++ b/src/libopensc/card-cac-common.h
|
|
|
b7ace2 |
@@ -0,0 +1,89 @@
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * card-cac-common.h: Code shared among CAC1 and CAC2 drivers
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * Copyright (C) 2018, Red Hat, Inc.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * Author: Jakub Jelen <jjelen@redhat.com>
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is free software; you can redistribute it and/or
|
|
|
b7ace2 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License as published by the Free Software Foundation; either
|
|
|
b7ace2 |
+ * version 2.1 of the License, or (at your option) any later version.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is distributed in the hope that it will be useful,
|
|
|
b7ace2 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
b7ace2 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
b7ace2 |
+ * Lesser General Public License for more details.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License along with this library; if not, write to the Free Software
|
|
|
b7ace2 |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#ifndef HAVE_CARD_CAC_COMMON_H
|
|
|
b7ace2 |
+#define HAVE_CARD_CAC_COMMON_H
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+typedef struct cac_cuid {
|
|
|
b7ace2 |
+ u8 gsc_rid[5];
|
|
|
b7ace2 |
+ u8 manufacturer_id;
|
|
|
b7ace2 |
+ u8 card_type;
|
|
|
b7ace2 |
+ u8 card_id;
|
|
|
b7ace2 |
+} cac_cuid_t;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* data structures to store meta data about CAC objects */
|
|
|
b7ace2 |
+typedef struct cac_object {
|
|
|
b7ace2 |
+ const char *name;
|
|
|
b7ace2 |
+ int fd;
|
|
|
b7ace2 |
+ sc_path_t path;
|
|
|
b7ace2 |
+} cac_object_t;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * CAC private data per card state
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+typedef struct cac_private_data {
|
|
|
b7ace2 |
+ int object_type; /* select set this so we know how to read the file */
|
|
|
b7ace2 |
+ int cert_next; /* index number for the next certificate found in the list */
|
|
|
b7ace2 |
+ u8 *cache_buf; /* cached version of the currently selected file */
|
|
|
b7ace2 |
+ size_t cache_buf_len; /* length of the cached selected file */
|
|
|
b7ace2 |
+ int cached; /* is the cached selected file valid */
|
|
|
b7ace2 |
+ cac_cuid_t cuid; /* card unique ID from the CCC */
|
|
|
b7ace2 |
+ u8 *cac_id; /* card serial number */
|
|
|
b7ace2 |
+ size_t cac_id_len; /* card serial number len */
|
|
|
b7ace2 |
+ list_t pki_list; /* list of pki containers */
|
|
|
b7ace2 |
+ cac_object_t *pki_current; /* current pki object _ctl function */
|
|
|
b7ace2 |
+ list_t general_list; /* list of general containers */
|
|
|
b7ace2 |
+ cac_object_t *general_current; /* current object for _ctl function */
|
|
|
b7ace2 |
+ sc_path_t *aca_path; /* ACA path to be selected before pin verification */
|
|
|
b7ace2 |
+} cac_private_data_t;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * Set up the normal CAC paths
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+#define CAC_1_RID "\xA0\x00\x00\x00\x79"
|
|
|
b7ace2 |
+#define CAC_TO_AID(x) x, sizeof(x)-1
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* template for a CAC pki object */
|
|
|
b7ace2 |
+static const cac_object_t cac_cac_pki_obj = {
|
|
|
b7ace2 |
+ "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
b7ace2 |
+ { CAC_TO_AID(CAC_1_RID "\x01\x00") } }
|
|
|
b7ace2 |
+};
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* template for emulated cuid */
|
|
|
b7ace2 |
+static const cac_cuid_t cac_cac_cuid = {
|
|
|
b7ace2 |
+ { 0xa0, 0x00, 0x00, 0x00, 0x79 },
|
|
|
b7ace2 |
+ 2, 2, 0
|
|
|
b7ace2 |
+};
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+cac_private_data_t *cac_new_private_data(void);
|
|
|
b7ace2 |
+void cac_free_private_data(cac_private_data_t *priv);
|
|
|
b7ace2 |
+int cac_add_object_to_list(list_t *list, const cac_object_t *object);
|
|
|
b7ace2 |
+const char *get_cac_label(int index);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#endif /* HAVE_CARD_CAC_COMMON_H */
|
|
|
b7ace2 |
diff --git a/src/libopensc/card-cac.c b/src/libopensc/card-cac.c
|
|
|
b7ace2 |
index bd4e0336..f9620009 100644
|
|
|
b7ace2 |
--- a/src/libopensc/card-cac.c
|
|
|
b7ace2 |
+++ b/src/libopensc/card-cac.c
|
|
|
b7ace2 |
@@ -58,8 +58,8 @@
|
|
|
b7ace2 |
#include "compression.h"
|
|
|
b7ace2 |
#endif
|
|
|
b7ace2 |
#include "iso7816.h"
|
|
|
b7ace2 |
+#include "card-cac-common.h"
|
|
|
b7ace2 |
|
|
|
b7ace2 |
-#define CAC_MAX_SIZE 4096 /* arbitrary, just needs to be 'large enough' */
|
|
|
b7ace2 |
/*
|
|
|
b7ace2 |
* CAC hardware and APDU constants
|
|
|
b7ace2 |
*/
|
|
|
b7ace2 |
@@ -147,20 +147,6 @@ typedef struct cac_card_url {
|
|
|
b7ace2 |
u8 keyCryptoAlgorithm; /* not used for VM cards */
|
|
|
b7ace2 |
} cac_card_url_t;
|
|
|
b7ace2 |
|
|
|
b7ace2 |
-typedef struct cac_cuid {
|
|
|
b7ace2 |
- u8 gsc_rid[5];
|
|
|
b7ace2 |
- u8 manufacturer_id;
|
|
|
b7ace2 |
- u8 card_type;
|
|
|
b7ace2 |
- u8 card_id;
|
|
|
b7ace2 |
-} cac_cuid_t;
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-/* data structures to store meta data about CAC objects */
|
|
|
b7ace2 |
-typedef struct cac_object {
|
|
|
b7ace2 |
- const char *name;
|
|
|
b7ace2 |
- int fd;
|
|
|
b7ace2 |
- sc_path_t path;
|
|
|
b7ace2 |
-} cac_object_t;
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
#define CAC_MAX_OBJECTS 16
|
|
|
b7ace2 |
|
|
|
b7ace2 |
typedef struct {
|
|
|
b7ace2 |
@@ -190,82 +176,10 @@ typedef struct {
|
|
|
b7ace2 |
#define CAC_OBJECT_TYPE_TLV_FILE 4
|
|
|
b7ace2 |
#define CAC_OBJECT_TYPE_GENERIC 5
|
|
|
b7ace2 |
|
|
|
b7ace2 |
-/*
|
|
|
b7ace2 |
- * CAC private data per card state
|
|
|
b7ace2 |
- */
|
|
|
b7ace2 |
-typedef struct cac_private_data {
|
|
|
b7ace2 |
- int object_type; /* select set this so we know how to read the file */
|
|
|
b7ace2 |
- int cert_next; /* index number for the next certificate found in the list */
|
|
|
b7ace2 |
- u8 *cache_buf; /* cached version of the currently selected file */
|
|
|
b7ace2 |
- size_t cache_buf_len; /* length of the cached selected file */
|
|
|
b7ace2 |
- int cached; /* is the cached selected file valid */
|
|
|
b7ace2 |
- cac_cuid_t cuid; /* card unique ID from the CCC */
|
|
|
b7ace2 |
- u8 *cac_id; /* card serial number */
|
|
|
b7ace2 |
- size_t cac_id_len; /* card serial number len */
|
|
|
b7ace2 |
- list_t pki_list; /* list of pki containers */
|
|
|
b7ace2 |
- cac_object_t *pki_current; /* current pki object _ctl function */
|
|
|
b7ace2 |
- list_t general_list; /* list of general containers */
|
|
|
b7ace2 |
- cac_object_t *general_current; /* current object for _ctl function */
|
|
|
b7ace2 |
- sc_path_t *aca_path; /* ACA path to be selected before pin verification */
|
|
|
b7ace2 |
-} cac_private_data_t;
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-#define CAC_DATA(card) ((cac_private_data_t*)card->drv_data)
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-static int cac_list_compare_path(const void *a, const void *b)
|
|
|
b7ace2 |
-{
|
|
|
b7ace2 |
- if (a == NULL || b == NULL)
|
|
|
b7ace2 |
- return 1;
|
|
|
b7ace2 |
- return memcmp( &((cac_object_t *) a)->path,
|
|
|
b7ace2 |
- &((cac_object_t *) b)->path, sizeof(sc_path_t));
|
|
|
b7ace2 |
-}
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-/* For SimCList autocopy, we need to know the size of the data elements */
|
|
|
b7ace2 |
-static size_t cac_list_meter(const void *el) {
|
|
|
b7ace2 |
- return sizeof(cac_object_t);
|
|
|
b7ace2 |
-}
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-static cac_private_data_t *cac_new_private_data(void)
|
|
|
b7ace2 |
-{
|
|
|
b7ace2 |
- cac_private_data_t *priv;
|
|
|
b7ace2 |
- priv = calloc(1, sizeof(cac_private_data_t));
|
|
|
b7ace2 |
- if (!priv)
|
|
|
b7ace2 |
- return NULL;
|
|
|
b7ace2 |
- list_init(&priv->pki_list);
|
|
|
b7ace2 |
- list_attributes_comparator(&priv->pki_list, cac_list_compare_path);
|
|
|
b7ace2 |
- list_attributes_copy(&priv->pki_list, cac_list_meter, 1);
|
|
|
b7ace2 |
- list_init(&priv->general_list);
|
|
|
b7ace2 |
- list_attributes_comparator(&priv->general_list, cac_list_compare_path);
|
|
|
b7ace2 |
- list_attributes_copy(&priv->general_list, cac_list_meter, 1);
|
|
|
b7ace2 |
- /* set other fields as appropriate */
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
- return priv;
|
|
|
b7ace2 |
-}
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-static void cac_free_private_data(cac_private_data_t *priv)
|
|
|
b7ace2 |
-{
|
|
|
b7ace2 |
- free(priv->cac_id);
|
|
|
b7ace2 |
- free(priv->cache_buf);
|
|
|
b7ace2 |
- free(priv->aca_path);
|
|
|
b7ace2 |
- list_destroy(&priv->pki_list);
|
|
|
b7ace2 |
- list_destroy(&priv->general_list);
|
|
|
b7ace2 |
- free(priv);
|
|
|
b7ace2 |
- return;
|
|
|
b7ace2 |
-}
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-static int cac_add_object_to_list(list_t *list, const cac_object_t *object)
|
|
|
b7ace2 |
-{
|
|
|
b7ace2 |
- if (list_append(list, object) < 0)
|
|
|
b7ace2 |
- return SC_ERROR_UNKNOWN;
|
|
|
b7ace2 |
- return SC_SUCCESS;
|
|
|
b7ace2 |
-}
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
/*
|
|
|
b7ace2 |
* Set up the normal CAC paths
|
|
|
b7ace2 |
*/
|
|
|
b7ace2 |
-#define CAC_TO_AID(x) x, sizeof(x)-1
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
#define CAC_2_RID "\xA0\x00\x00\x01\x16"
|
|
|
b7ace2 |
-#define CAC_1_RID "\xA0\x00\x00\x00\x79"
|
|
|
b7ace2 |
|
|
|
b7ace2 |
static const sc_path_t cac_ACA_Path = {
|
|
|
b7ace2 |
"", 0,
|
|
|
b7ace2 |
@@ -279,39 +193,6 @@ static const sc_path_t cac_CCC_Path = {
|
|
|
b7ace2 |
{ CAC_TO_AID(CAC_2_RID "\xDB\x00") }
|
|
|
b7ace2 |
};
|
|
|
b7ace2 |
|
|
|
b7ace2 |
-#define MAX_CAC_SLOTS 16 /* Maximum number of slots is 16 now */
|
|
|
b7ace2 |
-/* default certificate labels for the CAC card */
|
|
|
b7ace2 |
-static const char *cac_labels[MAX_CAC_SLOTS] = {
|
|
|
b7ace2 |
- "CAC ID Certificate",
|
|
|
b7ace2 |
- "CAC Email Signature Certificate",
|
|
|
b7ace2 |
- "CAC Email Encryption Certificate",
|
|
|
b7ace2 |
- "CAC Cert 4",
|
|
|
b7ace2 |
- "CAC Cert 5",
|
|
|
b7ace2 |
- "CAC Cert 6",
|
|
|
b7ace2 |
- "CAC Cert 7",
|
|
|
b7ace2 |
- "CAC Cert 8",
|
|
|
b7ace2 |
- "CAC Cert 9",
|
|
|
b7ace2 |
- "CAC Cert 10",
|
|
|
b7ace2 |
- "CAC Cert 11",
|
|
|
b7ace2 |
- "CAC Cert 12",
|
|
|
b7ace2 |
- "CAC Cert 13",
|
|
|
b7ace2 |
- "CAC Cert 14",
|
|
|
b7ace2 |
- "CAC Cert 15",
|
|
|
b7ace2 |
- "CAC Cert 16"
|
|
|
b7ace2 |
-};
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-/* template for a CAC pki object */
|
|
|
b7ace2 |
-static const cac_object_t cac_cac_pki_obj = {
|
|
|
b7ace2 |
- "CAC Certificate", 0x0, { { 0 }, 0, 0, 0, SC_PATH_TYPE_DF_NAME,
|
|
|
b7ace2 |
- { CAC_TO_AID(CAC_1_RID "\x01\x00") } }
|
|
|
b7ace2 |
-};
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
-/* template for emulated cuid */
|
|
|
b7ace2 |
-static const cac_cuid_t cac_cac_cuid = {
|
|
|
b7ace2 |
- { 0xa0, 0x00, 0x00, 0x00, 0x79 },
|
|
|
b7ace2 |
- 2, 2, 0
|
|
|
b7ace2 |
-};
|
|
|
b7ace2 |
-
|
|
|
b7ace2 |
/*
|
|
|
b7ace2 |
* CAC general objects defined in 4.3.1.2 of CAC Applet Developer Guide Version 1.0.
|
|
|
b7ace2 |
* doubles as a source for CAC-2 labels.
|
|
|
b7ace2 |
@@ -1178,7 +1059,7 @@ static int cac_get_properties(sc_card_t *card, cac_properties_t *prop)
|
|
|
b7ace2 |
*
|
|
|
b7ace2 |
* The rest is just copied from iso7816_select_file
|
|
|
b7ace2 |
*/
|
|
|
b7ace2 |
-static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out, int type)
|
|
|
b7ace2 |
+static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
|
|
b7ace2 |
{
|
|
|
b7ace2 |
struct sc_context *ctx;
|
|
|
b7ace2 |
struct sc_apdu apdu;
|
|
|
b7ace2 |
@@ -1356,7 +1237,7 @@ static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc
|
|
|
b7ace2 |
|
|
|
b7ace2 |
static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
|
|
b7ace2 |
{
|
|
|
b7ace2 |
- return cac_select_file_by_type(card, in_path, file_out, card->type);
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, in_path, file_out);
|
|
|
b7ace2 |
}
|
|
|
b7ace2 |
|
|
|
b7ace2 |
static int cac_finish(sc_card_t *card)
|
|
|
b7ace2 |
@@ -1374,13 +1255,13 @@ static int cac_finish(sc_card_t *card)
|
|
|
b7ace2 |
/* select the Card Capabilities Container on CAC-2 */
|
|
|
b7ace2 |
static int cac_select_CCC(sc_card_t *card)
|
|
|
b7ace2 |
{
|
|
|
b7ace2 |
- return cac_select_file_by_type(card, &cac_CCC_Path, NULL, SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, &cac_CCC_Path, NULL);
|
|
|
b7ace2 |
}
|
|
|
b7ace2 |
|
|
|
b7ace2 |
/* Select ACA in non-standard location */
|
|
|
b7ace2 |
static int cac_select_ACA(sc_card_t *card)
|
|
|
b7ace2 |
{
|
|
|
b7ace2 |
- return cac_select_file_by_type(card, &cac_ACA_Path, NULL, SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, &cac_ACA_Path, NULL);
|
|
|
b7ace2 |
}
|
|
|
b7ace2 |
|
|
|
b7ace2 |
static int cac_path_from_cardurl(sc_card_t *card, sc_path_t *path, cac_card_url_t *val, int len)
|
|
|
b7ace2 |
@@ -1432,7 +1313,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
|
|
|
b7ace2 |
/* Call without OID set will just select the AID without subseqent
|
|
|
b7ace2 |
* OID selection, which we need to figure out just now
|
|
|
b7ace2 |
*/
|
|
|
b7ace2 |
- cac_select_file_by_type(card, &new_object.path, NULL, SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ cac_select_file_by_type(card, &new_object.path, NULL);
|
|
|
b7ace2 |
r = cac_get_properties(card, &prop);
|
|
|
b7ace2 |
if (r < 0)
|
|
|
b7ace2 |
return SC_ERROR_INTERNAL;
|
|
|
b7ace2 |
@@ -1444,7 +1325,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
|
|
|
b7ace2 |
|
|
|
b7ace2 |
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
"ACA: pki_object found, cert_next=%d (%s), privkey=%d",
|
|
|
b7ace2 |
- priv->cert_next, cac_labels[priv->cert_next],
|
|
|
b7ace2 |
+ priv->cert_next, get_cac_label(priv->cert_next),
|
|
|
b7ace2 |
prop.objects[i].privatekey);
|
|
|
b7ace2 |
|
|
|
b7ace2 |
/* If the private key is not initialized, we can safely
|
|
|
b7ace2 |
@@ -1460,7 +1341,7 @@ static int cac_parse_aid(sc_card_t *card, cac_private_data_t *priv, u8 *aid, int
|
|
|
b7ace2 |
memcpy(new_object.path.value, &prop.objects[i].oid, 2);
|
|
|
b7ace2 |
new_object.path.len = 2;
|
|
|
b7ace2 |
new_object.path.type = SC_PATH_TYPE_FILE_ID;
|
|
|
b7ace2 |
- new_object.name = cac_labels[priv->cert_next];
|
|
|
b7ace2 |
+ new_object.name = get_cac_label(priv->cert_next);
|
|
|
b7ace2 |
new_object.fd = priv->cert_next+1;
|
|
|
b7ace2 |
cac_add_object_to_list(&priv->pki_list, &new_object);
|
|
|
b7ace2 |
priv->cert_next++;
|
|
|
b7ace2 |
@@ -1488,7 +1369,7 @@ static int cac_parse_cardurl(sc_card_t *card, cac_private_data_t *priv, cac_card
|
|
|
b7ace2 |
*/
|
|
|
b7ace2 |
if (priv->cert_next >= MAX_CAC_SLOTS)
|
|
|
b7ace2 |
break; /* don't fail just because we have more certs than we can support */
|
|
|
b7ace2 |
- new_object.name = cac_labels[priv->cert_next];
|
|
|
b7ace2 |
+ new_object.name = get_cac_label(priv->cert_next);
|
|
|
b7ace2 |
new_object.fd = priv->cert_next+1;
|
|
|
b7ace2 |
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"CARDURL: pki_object found, cert_next=%d (%s),", priv->cert_next, new_object.name);
|
|
|
b7ace2 |
cac_add_object_to_list(&priv->pki_list, &new_object);
|
|
|
b7ace2 |
@@ -1629,7 +1510,7 @@ static int cac_parse_CCC(sc_card_t *card, cac_private_data_t *priv, u8 *tl,
|
|
|
b7ace2 |
if (r < 0)
|
|
|
b7ace2 |
return r;
|
|
|
b7ace2 |
|
|
|
b7ace2 |
- r = cac_select_file_by_type(card, &new_path, NULL, SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ r = cac_select_file_by_type(card, &new_path, NULL);
|
|
|
b7ace2 |
if (r < 0)
|
|
|
b7ace2 |
return r;
|
|
|
b7ace2 |
|
|
|
b7ace2 |
@@ -1740,7 +1621,7 @@ static int cac_select_pki_applet(sc_card_t *card, int index)
|
|
|
b7ace2 |
{
|
|
|
b7ace2 |
sc_path_t applet_path = cac_cac_pki_obj.path;
|
|
|
b7ace2 |
applet_path.aid.value[applet_path.aid.len-1] = index;
|
|
|
b7ace2 |
- return cac_select_file_by_type(card, &applet_path, NULL, SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, &applet_path, NULL);
|
|
|
b7ace2 |
}
|
|
|
b7ace2 |
|
|
|
b7ace2 |
/*
|
|
|
b7ace2 |
@@ -1785,7 +1666,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *
|
|
|
b7ace2 |
for (i = index; i < MAX_CAC_SLOTS; i++) {
|
|
|
b7ace2 |
r = cac_select_pki_applet(card, i);
|
|
|
b7ace2 |
if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
- pki_obj.name = cac_labels[i];
|
|
|
b7ace2 |
+ pki_obj.name = get_cac_label(i);
|
|
|
b7ace2 |
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
"CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name);
|
|
|
b7ace2 |
pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i;
|
|
|
b7ace2 |
@@ -1796,8 +1677,7 @@ static int cac_populate_cac_alt(sc_card_t *card, int index, cac_private_data_t *
|
|
|
b7ace2 |
|
|
|
b7ace2 |
/* populate non-PKI objects */
|
|
|
b7ace2 |
for (i=0; i < cac_object_count; i++) {
|
|
|
b7ace2 |
- r = cac_select_file_by_type(card, &cac_objects[i].path, NULL,
|
|
|
b7ace2 |
- SC_CARD_TYPE_CAC_II);
|
|
|
b7ace2 |
+ r = cac_select_file_by_type(card, &cac_objects[i].path, NULL);
|
|
|
b7ace2 |
if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
"CAC: obj_object found, cert_next=%d (%s),",
|
|
|
b7ace2 |
diff --git a/src/libopensc/card-cac1.c b/src/libopensc/card-cac1.c
|
|
|
b7ace2 |
new file mode 100644
|
|
|
b7ace2 |
index 00000000..f3a16547
|
|
|
b7ace2 |
--- /dev/null
|
|
|
b7ace2 |
+++ b/src/libopensc/card-cac1.c
|
|
|
b7ace2 |
@@ -0,0 +1,563 @@
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * card-cac1.c: Support for legacy CAC-1
|
|
|
b7ace2 |
+ * card-default.c: Support for cards with no driver
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
|
|
|
b7ace2 |
+ * Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
|
|
|
b7ace2 |
+ * Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
|
|
|
b7ace2 |
+ * Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
|
|
|
b7ace2 |
+ * Copyright (C) 2016 - 2018, Red Hat, Inc.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * CAC driver author: Robert Relyea <rrelyea@redhat.com>
|
|
|
b7ace2 |
+ * Further work: Jakub Jelen <jjelen@redhat.com>
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is free software; you can redistribute it and/or
|
|
|
b7ace2 |
+ * modify it under the terms of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License as published by the Free Software Foundation; either
|
|
|
b7ace2 |
+ * version 2.1 of the License, or (at your option) any later version.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * This library is distributed in the hope that it will be useful,
|
|
|
b7ace2 |
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
b7ace2 |
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
b7ace2 |
+ * Lesser General Public License for more details.
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * You should have received a copy of the GNU Lesser General Public
|
|
|
b7ace2 |
+ * License along with this library; if not, write to the Free Software
|
|
|
b7ace2 |
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#if HAVE_CONFIG_H
|
|
|
b7ace2 |
+#include "config.h"
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#include <ctype.h>
|
|
|
b7ace2 |
+#include <fcntl.h>
|
|
|
b7ace2 |
+#include <limits.h>
|
|
|
b7ace2 |
+#include <stdlib.h>
|
|
|
b7ace2 |
+#include <string.h>
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#ifdef _WIN32
|
|
|
b7ace2 |
+#include <io.h>
|
|
|
b7ace2 |
+#else
|
|
|
b7ace2 |
+#include <unistd.h>
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#ifdef ENABLE_OPENSSL
|
|
|
b7ace2 |
+ /* openssl only needed for card administration */
|
|
|
b7ace2 |
+#include <openssl/evp.h>
|
|
|
b7ace2 |
+#include <openssl/bio.h>
|
|
|
b7ace2 |
+#include <openssl/pem.h>
|
|
|
b7ace2 |
+#include <openssl/rand.h>
|
|
|
b7ace2 |
+#include <openssl/rsa.h>
|
|
|
b7ace2 |
+#endif /* ENABLE_OPENSSL */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+#include "internal.h"
|
|
|
b7ace2 |
+#include "simpletlv.h"
|
|
|
b7ace2 |
+#include "cardctl.h"
|
|
|
b7ace2 |
+#ifdef ENABLE_ZLIB
|
|
|
b7ace2 |
+#include "compression.h"
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+#include "iso7816.h"
|
|
|
b7ace2 |
+#include "card-cac-common.h"
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * CAC hardware and APDU constants
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+#define CAC_INS_GET_CERTIFICATE 0x36 /* CAC1 command to read a certificate */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * OLD cac read certificate, only use with CAC-1 card.
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+static int cac_cac1_get_certificate(sc_card_t *card, u8 **out_buf, size_t *out_len)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ u8 buf[CAC_MAX_SIZE];
|
|
|
b7ace2 |
+ u8 *out_ptr;
|
|
|
b7ace2 |
+ size_t size = 0;
|
|
|
b7ace2 |
+ size_t left = 0;
|
|
|
b7ace2 |
+ size_t len, next_len;
|
|
|
b7ace2 |
+ sc_apdu_t apdu;
|
|
|
b7ace2 |
+ int r = SC_SUCCESS;
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+ /* get the size */
|
|
|
b7ace2 |
+ size = left = *out_buf ? *out_len : sizeof(buf);
|
|
|
b7ace2 |
+ out_ptr = *out_buf ? *out_buf : buf;
|
|
|
b7ace2 |
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, CAC_INS_GET_CERTIFICATE, 0, 0 );
|
|
|
b7ace2 |
+ next_len = MIN(left, 100);
|
|
|
b7ace2 |
+ for (; left > 0; left -= len, out_ptr += len) {
|
|
|
b7ace2 |
+ len = next_len;
|
|
|
b7ace2 |
+ apdu.resp = out_ptr;
|
|
|
b7ace2 |
+ apdu.le = len;
|
|
|
b7ace2 |
+ apdu.resplen = left;
|
|
|
b7ace2 |
+ r = sc_transmit_apdu(card, &apdu);
|
|
|
b7ace2 |
+ if (r < 0) {
|
|
|
b7ace2 |
+ break;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ if (apdu.resplen == 0) {
|
|
|
b7ace2 |
+ r = SC_ERROR_INTERNAL;
|
|
|
b7ace2 |
+ break;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ /* in the old CAC-1, 0x63 means 'more data' in addition to 'pin failed' */
|
|
|
b7ace2 |
+ if (apdu.sw1 != 0x63) {
|
|
|
b7ace2 |
+ /* we've either finished reading, or hit an error, break */
|
|
|
b7ace2 |
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
b7ace2 |
+ left -= len;
|
|
|
b7ace2 |
+ break;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ next_len = MIN(left, apdu.sw2);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ if (r < 0) {
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ r = size - left;
|
|
|
b7ace2 |
+ if (*out_buf == NULL) {
|
|
|
b7ace2 |
+ *out_buf = malloc(r);
|
|
|
b7ace2 |
+ if (*out_buf == NULL) {
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
+ SC_ERROR_OUT_OF_MEMORY);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ memcpy(*out_buf, buf, r);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ *out_len = r;
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * Callers of this may be expecting a certificate,
|
|
|
b7ace2 |
+ * select file will have saved the object type for us
|
|
|
b7ace2 |
+ * as well as set that we want the cert from the object.
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+static int cac_read_binary(sc_card_t *card, unsigned int idx,
|
|
|
b7ace2 |
+ unsigned char *buf, size_t count, unsigned long flags)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ cac_private_data_t * priv = CAC_DATA(card);
|
|
|
b7ace2 |
+ int r = 0;
|
|
|
b7ace2 |
+ u8 *val = NULL;
|
|
|
b7ace2 |
+ u8 *cert_ptr;
|
|
|
b7ace2 |
+ size_t val_len;
|
|
|
b7ace2 |
+ size_t len, cert_len;
|
|
|
b7ace2 |
+ u8 cert_type;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* if we didn't return it all last time, return the remainder */
|
|
|
b7ace2 |
+ if (priv->cached) {
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
b7ace2 |
+ "returning cached value idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
b7ace2 |
+ idx, count);
|
|
|
b7ace2 |
+ if (idx > priv->cache_buf_len) {
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_FILE_END_REACHED);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ len = MIN(count, priv->cache_buf_len-idx);
|
|
|
b7ace2 |
+ memcpy(buf, &priv->cache_buf[idx], len);
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, len);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,
|
|
|
b7ace2 |
+ "clearing cache idx=%d count=%"SC_FORMAT_LEN_SIZE_T"u",
|
|
|
b7ace2 |
+ idx, count);
|
|
|
b7ace2 |
+ if (priv->cache_buf) {
|
|
|
b7ace2 |
+ free(priv->cache_buf);
|
|
|
b7ace2 |
+ priv->cache_buf = NULL;
|
|
|
b7ace2 |
+ priv->cache_buf_len = 0;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = cac_cac1_get_certificate(card, &val, &val_len);
|
|
|
b7ace2 |
+ if (r < 0)
|
|
|
b7ace2 |
+ goto done;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ cert_type = val[0];
|
|
|
b7ace2 |
+ cert_ptr = val + 1;
|
|
|
b7ace2 |
+ cert_len = val_len - 1;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* if the info byte is 1, then the cert is compressed, decompress it */
|
|
|
b7ace2 |
+ if ((cert_type & 0x3) == 1) {
|
|
|
b7ace2 |
+#ifdef ENABLE_ZLIB
|
|
|
b7ace2 |
+ r = sc_decompress_alloc(&priv->cache_buf, &priv->cache_buf_len,
|
|
|
b7ace2 |
+ cert_ptr, cert_len, COMPRESSION_AUTO);
|
|
|
b7ace2 |
+#else
|
|
|
b7ace2 |
+ sc_log(card->ctx, "CAC compression not supported, no zlib");
|
|
|
b7ace2 |
+ r = SC_ERROR_NOT_SUPPORTED;
|
|
|
b7ace2 |
+#endif
|
|
|
b7ace2 |
+ if (r)
|
|
|
b7ace2 |
+ goto done;
|
|
|
b7ace2 |
+ cert_ptr = val;
|
|
|
b7ace2 |
+ } else if (cert_len > 0) {
|
|
|
b7ace2 |
+ priv->cache_buf = malloc(cert_len);
|
|
|
b7ace2 |
+ if (priv->cache_buf == NULL) {
|
|
|
b7ace2 |
+ r = SC_ERROR_OUT_OF_MEMORY;
|
|
|
b7ace2 |
+ goto done;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ priv->cache_buf_len = cert_len;
|
|
|
b7ace2 |
+ memcpy(priv->cache_buf, cert_ptr, cert_len);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* OK we've read the data, now copy the required portion out to the callers buffer */
|
|
|
b7ace2 |
+ priv->cached = 1;
|
|
|
b7ace2 |
+ len = MIN(count, priv->cache_buf_len-idx);
|
|
|
b7ace2 |
+ memcpy(buf, &priv->cache_buf[idx], len);
|
|
|
b7ace2 |
+ r = len;
|
|
|
b7ace2 |
+done:
|
|
|
b7ace2 |
+ if (val)
|
|
|
b7ace2 |
+ free(val);
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, r);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * CAC cards use SC_PATH_SELECT_OBJECT_ID rather than SC_PATH_SELECT_FILE_ID. In order to use more
|
|
|
b7ace2 |
+ * of the PKCS #15 structure, we call the selection SC_PATH_SELECT_FILE_ID, but we set p1 to 2 instead
|
|
|
b7ace2 |
+ * of 0. Also cac1 does not do any FCI, but it doesn't understand not selecting it. It returns invalid INS
|
|
|
b7ace2 |
+ * if it doesn't like anything about the select, so we always 'request' FCI for CAC1
|
|
|
b7ace2 |
+ *
|
|
|
b7ace2 |
+ * The rest is just copied from iso7816_select_file
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+static int cac_select_file_by_type(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ struct sc_context *ctx;
|
|
|
b7ace2 |
+ struct sc_apdu apdu;
|
|
|
b7ace2 |
+ unsigned char buf[SC_MAX_APDU_BUFFER_SIZE];
|
|
|
b7ace2 |
+ unsigned char pathbuf[SC_MAX_PATH_SIZE], *path = pathbuf;
|
|
|
b7ace2 |
+ int r, pathlen, pathtype;
|
|
|
b7ace2 |
+ struct sc_file *file = NULL;
|
|
|
b7ace2 |
+ cac_private_data_t * priv = CAC_DATA(card);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ assert(card != NULL && in_path != NULL);
|
|
|
b7ace2 |
+ ctx = card->ctx;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ memcpy(path, in_path->value, in_path->len);
|
|
|
b7ace2 |
+ pathlen = in_path->len;
|
|
|
b7ace2 |
+ pathtype = in_path->type;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
+ "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)",
|
|
|
b7ace2 |
+ in_path->aid.value[0], in_path->aid.value[1],
|
|
|
b7ace2 |
+ in_path->aid.value[2], in_path->aid.value[3],
|
|
|
b7ace2 |
+ in_path->aid.value[4], in_path->aid.value[5],
|
|
|
b7ace2 |
+ in_path->aid.value[6], in_path->aid.len, in_path->value[0],
|
|
|
b7ace2 |
+ in_path->value[1], in_path->value[2], in_path->value[3],
|
|
|
b7ace2 |
+ in_path->len, in_path->type, in_path->type);
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "file_out=%p index=%d count=%d\n",
|
|
|
b7ace2 |
+ file_out, in_path->index, in_path->count);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* Sigh, sc_key_select expects paths to keys to have specific formats. There is no override.
|
|
|
b7ace2 |
+ * we have to add some bytes to the path to make it happy. A better fix would be to give sc_key_file
|
|
|
b7ace2 |
+ * a flag that says 'no, really this path is fine'. We only need to do this for private keys */
|
|
|
b7ace2 |
+ if ((pathlen > 2) && (pathlen <= 4) && memcmp(path, "\x3F\x00", 2) == 0) {
|
|
|
b7ace2 |
+ if (pathlen > 2) {
|
|
|
b7ace2 |
+ path += 2;
|
|
|
b7ace2 |
+ pathlen -= 2;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* CAC has multiple different type of objects that aren't PKCS #15. When we read
|
|
|
b7ace2 |
+ * them we need convert them to something PKCS #15 would understand. Find the object
|
|
|
b7ace2 |
+ * and object type here:
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+ if (priv) { /* don't record anything if we haven't been initialized yet */
|
|
|
b7ace2 |
+ /* forget any old cached values */
|
|
|
b7ace2 |
+ if (priv->cache_buf) {
|
|
|
b7ace2 |
+ free(priv->cache_buf);
|
|
|
b7ace2 |
+ priv->cache_buf = NULL;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ priv->cache_buf_len = 0;
|
|
|
b7ace2 |
+ priv->cached = 0;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ if (in_path->aid.len) {
|
|
|
b7ace2 |
+ if (!pathlen) {
|
|
|
b7ace2 |
+ memcpy(path, in_path->aid.value, in_path->aid.len);
|
|
|
b7ace2 |
+ pathlen = in_path->aid.len;
|
|
|
b7ace2 |
+ pathtype = SC_PATH_TYPE_DF_NAME;
|
|
|
b7ace2 |
+ } else {
|
|
|
b7ace2 |
+ /* First, select the application */
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,"select application" );
|
|
|
b7ace2 |
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, 4, 0);
|
|
|
b7ace2 |
+ apdu.data = in_path->aid.value;
|
|
|
b7ace2 |
+ apdu.datalen = in_path->aid.len;
|
|
|
b7ace2 |
+ apdu.lc = in_path->aid.len;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = sc_transmit_apdu(card, &apdu);
|
|
|
b7ace2 |
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
|
|
|
b7ace2 |
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
b7ace2 |
+ if (r)
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, r);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ switch (pathtype) {
|
|
|
b7ace2 |
+ /* ideally we would had SC_PATH_TYPE_OBJECT_ID and add code to the iso7816 select.
|
|
|
b7ace2 |
+ * Unfortunately we'd also need to update the caching code as well. For now just
|
|
|
b7ace2 |
+ * use FILE_ID and change p1 here */
|
|
|
b7ace2 |
+ case SC_PATH_TYPE_FILE_ID:
|
|
|
b7ace2 |
+ apdu.p1 = 2;
|
|
|
b7ace2 |
+ if (pathlen != 2)
|
|
|
b7ace2 |
+ return SC_ERROR_INVALID_ARGUMENTS;
|
|
|
b7ace2 |
+ break;
|
|
|
b7ace2 |
+ case SC_PATH_TYPE_DF_NAME:
|
|
|
b7ace2 |
+ apdu.p1 = 4;
|
|
|
b7ace2 |
+ break;
|
|
|
b7ace2 |
+ default:
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, SC_ERROR_INVALID_ARGUMENTS);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ apdu.lc = pathlen;
|
|
|
b7ace2 |
+ apdu.data = path;
|
|
|
b7ace2 |
+ apdu.datalen = pathlen;
|
|
|
b7ace2 |
+ apdu.resp = buf;
|
|
|
b7ace2 |
+ apdu.resplen = sizeof(buf);
|
|
|
b7ace2 |
+ apdu.le = sc_get_max_recv_size(card) < 256 ? sc_get_max_recv_size(card) : 256;
|
|
|
b7ace2 |
+ apdu.p2 = 0x00;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = sc_transmit_apdu(card, &apdu);
|
|
|
b7ace2 |
+ LOG_TEST_RET(ctx, r, "APDU transmit failed");
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ if (file_out == NULL) {
|
|
|
b7ace2 |
+ /* For some cards 'SELECT' can be only with request to return FCI/FCP. */
|
|
|
b7ace2 |
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
b7ace2 |
+ if (apdu.sw1 == 0x6A && apdu.sw2 == 0x86) {
|
|
|
b7ace2 |
+ apdu.p2 = 0x00;
|
|
|
b7ace2 |
+ apdu.resplen = sizeof(buf);
|
|
|
b7ace2 |
+ if (sc_transmit_apdu(card, &apdu) == SC_SUCCESS)
|
|
|
b7ace2 |
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ if (apdu.sw1 == 0x61)
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, SC_SUCCESS);
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, r);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = sc_check_sw(card, apdu.sw1, apdu.sw2);
|
|
|
b7ace2 |
+ if (r)
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, r);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* CAC cards never return FCI, fake one */
|
|
|
b7ace2 |
+ file = sc_file_new();
|
|
|
b7ace2 |
+ if (file == NULL)
|
|
|
b7ace2 |
+ LOG_FUNC_RETURN(ctx, SC_ERROR_OUT_OF_MEMORY);
|
|
|
b7ace2 |
+ file->path = *in_path;
|
|
|
b7ace2 |
+ file->size = CAC_MAX_SIZE; /* we don't know how big, just give a large size until we can read the file */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ *file_out = file;
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static int cac_select_file(sc_card_t *card, const sc_path_t *in_path, sc_file_t **file_out)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, in_path, file_out);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static int cac_finish(sc_card_t *card)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ cac_private_data_t * priv = CAC_DATA(card);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+ if (priv) {
|
|
|
b7ace2 |
+ cac_free_private_data(priv);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ return SC_SUCCESS;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* select a CAC pki applet by index */
|
|
|
b7ace2 |
+static int cac_select_pki_applet(sc_card_t *card, int index)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ sc_path_t applet_path = cac_cac_pki_obj.path;
|
|
|
b7ace2 |
+ applet_path.aid.value[applet_path.aid.len-1] = index;
|
|
|
b7ace2 |
+ return cac_select_file_by_type(card, &applet_path, NULL);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * Find the first existing CAC applet. If none found, then this isn't a CAC
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+static int cac_find_first_pki_applet(sc_card_t *card, int *index_out)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ int r, i;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ for (i = 0; i < MAX_CAC_SLOTS; i++) {
|
|
|
b7ace2 |
+ r = cac_select_pki_applet(card, i);
|
|
|
b7ace2 |
+ if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
+ u8 data[2];
|
|
|
b7ace2 |
+ sc_apdu_t apdu;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* Try to read first two bytes of the buffer to
|
|
|
b7ace2 |
+ * make sure it is not just malfunctioning card
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+ sc_format_apdu(card, &apdu, SC_APDU_CASE_2,
|
|
|
b7ace2 |
+ CAC_INS_GET_CERTIFICATE, 0x00, 0x00);
|
|
|
b7ace2 |
+ apdu.le = 0x02;
|
|
|
b7ace2 |
+ apdu.resplen = 2;
|
|
|
b7ace2 |
+ apdu.resp = data;
|
|
|
b7ace2 |
+ r = sc_transmit_apdu(card, &apdu);
|
|
|
b7ace2 |
+ /* SW1 = 0x63 means more data in CAC1 */
|
|
|
b7ace2 |
+ if (r == SC_SUCCESS && apdu.sw1 != 0x63)
|
|
|
b7ace2 |
+ continue;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ *index_out = i;
|
|
|
b7ace2 |
+ return SC_SUCCESS;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ return SC_ERROR_OBJECT_NOT_FOUND;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static int cac_populate_cac1(sc_card_t *card, int index, cac_private_data_t *priv)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ int r, i;
|
|
|
b7ace2 |
+ cac_object_t pki_obj = cac_cac_pki_obj;
|
|
|
b7ace2 |
+ u8 buf[100];
|
|
|
b7ace2 |
+ u8 *val;
|
|
|
b7ace2 |
+ size_t val_len;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* populate PKI objects */
|
|
|
b7ace2 |
+ for (i = index; i < MAX_CAC_SLOTS; i++) {
|
|
|
b7ace2 |
+ r = cac_select_pki_applet(card, i);
|
|
|
b7ace2 |
+ if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
+ pki_obj.name = get_cac_label(i);
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
+ "CAC: pki_object found, cert_next=%d (%s),", i, pki_obj.name);
|
|
|
b7ace2 |
+ pki_obj.path.aid.value[pki_obj.path.aid.len-1] = i;
|
|
|
b7ace2 |
+ pki_obj.fd = i+1; /* don't use id of zero */
|
|
|
b7ace2 |
+ cac_add_object_to_list(&priv->pki_list, &pki_obj);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /*
|
|
|
b7ace2 |
+ * create a cuid to simulate the cac 2 cuid.
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+ priv->cuid = cac_cac_cuid;
|
|
|
b7ace2 |
+ /* create a serial number by hashing the first 100 bytes of the
|
|
|
b7ace2 |
+ * first certificate on the card */
|
|
|
b7ace2 |
+ r = cac_select_pki_applet(card, index);
|
|
|
b7ace2 |
+ if (r < 0) {
|
|
|
b7ace2 |
+ return r; /* shouldn't happen unless the card has been removed or is malfunctioning */
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ val = buf;
|
|
|
b7ace2 |
+ val_len = sizeof(buf);
|
|
|
b7ace2 |
+ r = cac_cac1_get_certificate(card, &val, &val_len);
|
|
|
b7ace2 |
+ if (r >= 0) {
|
|
|
b7ace2 |
+ priv->cac_id = malloc(20);
|
|
|
b7ace2 |
+ if (priv->cac_id == NULL) {
|
|
|
b7ace2 |
+ return SC_ERROR_OUT_OF_MEMORY;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+#ifdef ENABLE_OPENSSL
|
|
|
b7ace2 |
+ SHA1(val, val_len, priv->cac_id);
|
|
|
b7ace2 |
+ priv->cac_id_len = 20;
|
|
|
b7ace2 |
+ sc_debug_hex(card->ctx, SC_LOG_DEBUG_VERBOSE,
|
|
|
b7ace2 |
+ "cuid", priv->cac_id, priv->cac_id_len);
|
|
|
b7ace2 |
+#else
|
|
|
b7ace2 |
+ sc_log(card->ctx, "OpenSSL Required");
|
|
|
b7ace2 |
+ return SC_ERROR_NOT_SUPPORTED;
|
|
|
b7ace2 |
+#endif /* ENABLE_OPENSSL */
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ return SC_SUCCESS;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/*
|
|
|
b7ace2 |
+ * Look for a CAC card. If it exists, initialize our data structures
|
|
|
b7ace2 |
+ */
|
|
|
b7ace2 |
+static int cac_find_and_initialize(sc_card_t *card, int initialize)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ int r, index;
|
|
|
b7ace2 |
+ cac_private_data_t *priv = NULL;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* already initialized? */
|
|
|
b7ace2 |
+ if (card->drv_data) {
|
|
|
b7ace2 |
+ return SC_SUCCESS;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ /* is this a CAC Alt token without any accompanying structures */
|
|
|
b7ace2 |
+ r = cac_find_first_pki_applet(card, &index);
|
|
|
b7ace2 |
+ if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
+ sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "PKI applet found, is bare CAC-1");
|
|
|
b7ace2 |
+ if (!initialize) /* match card only */
|
|
|
b7ace2 |
+ return r;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ if (!priv) {
|
|
|
b7ace2 |
+ priv = cac_new_private_data();
|
|
|
b7ace2 |
+ if (!priv)
|
|
|
b7ace2 |
+ return SC_ERROR_OUT_OF_MEMORY;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ card->drv_data = priv; /* needed for the read_binary() */
|
|
|
b7ace2 |
+ r = cac_populate_cac1(card, index, priv);
|
|
|
b7ace2 |
+ if (r == SC_SUCCESS) {
|
|
|
b7ace2 |
+ card->type = SC_CARD_TYPE_CAC_I;
|
|
|
b7ace2 |
+ return r;
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ card->drv_data = NULL; /* reset on failure */
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ if (priv) {
|
|
|
b7ace2 |
+ cac_free_private_data(priv);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ return r;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+/* NOTE: returns a bool, 1 card matches, 0 it does not */
|
|
|
b7ace2 |
+static int cac_match_card(sc_card_t *card)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ int r;
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+ /* Since we send an APDU, the card's logout function may be called...
|
|
|
b7ace2 |
+ * however it may be in dirty memory */
|
|
|
b7ace2 |
+ card->ops->logout = NULL;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = cac_find_and_initialize(card, 0);
|
|
|
b7ace2 |
+ return (r == SC_SUCCESS); /* never match */
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static int cac_init(sc_card_t *card)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ int r;
|
|
|
b7ace2 |
+ unsigned long flags;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ r = cac_find_and_initialize(card, 1);
|
|
|
b7ace2 |
+ if (r < 0) {
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_INVALID_CARD);
|
|
|
b7ace2 |
+ }
|
|
|
b7ace2 |
+ flags = SC_ALGORITHM_RSA_RAW;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ _sc_card_add_rsa_alg(card, 1024, flags, 0); /* mandatory */
|
|
|
b7ace2 |
+ _sc_card_add_rsa_alg(card, 2048, flags, 0); /* optional */
|
|
|
b7ace2 |
+ _sc_card_add_rsa_alg(card, 3072, flags, 0); /* optional */
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_SUCCESS);
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static struct sc_card_operations cac_ops;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static struct sc_card_driver cac1_drv = {
|
|
|
b7ace2 |
+ "Common Access Card (CAC 1)",
|
|
|
b7ace2 |
+ "cac1",
|
|
|
b7ace2 |
+ &cac_ops,
|
|
|
b7ace2 |
+ NULL, 0, NULL
|
|
|
b7ace2 |
+};
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+static struct sc_card_driver * sc_get_driver(void)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ /* Inherit most of the things from the CAC driver */
|
|
|
b7ace2 |
+ struct sc_card_driver *cac_drv = sc_get_cac_driver();
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ cac_ops = *cac_drv->ops;
|
|
|
b7ace2 |
+ cac_ops.match_card = cac_match_card;
|
|
|
b7ace2 |
+ cac_ops.init = cac_init;
|
|
|
b7ace2 |
+ cac_ops.finish = cac_finish;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ cac_ops.select_file = cac_select_file; /* need to record object type */
|
|
|
b7ace2 |
+ cac_ops.read_binary = cac_read_binary;
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+ return &cac1_drv;
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+
|
|
|
b7ace2 |
+struct sc_card_driver * sc_get_cac1_driver(void)
|
|
|
b7ace2 |
+{
|
|
|
b7ace2 |
+ return sc_get_driver();
|
|
|
b7ace2 |
+}
|
|
|
b7ace2 |
diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h
|
|
|
b7ace2 |
index 5d545b35..f4df17fb 100644
|
|
|
b7ace2 |
--- a/src/libopensc/cards.h
|
|
|
b7ace2 |
+++ b/src/libopensc/cards.h
|
|
|
b7ace2 |
@@ -286,6 +286,7 @@ extern sc_card_driver_t *sc_get_gids_driver(void);
|
|
|
b7ace2 |
extern sc_card_driver_t *sc_get_jpki_driver(void);
|
|
|
b7ace2 |
extern sc_card_driver_t *sc_get_coolkey_driver(void);
|
|
|
b7ace2 |
extern sc_card_driver_t *sc_get_cac_driver(void);
|
|
|
b7ace2 |
+extern sc_card_driver_t *sc_get_cac1_driver(void);
|
|
|
b7ace2 |
extern sc_card_driver_t *sc_get_npa_driver(void);
|
|
|
b7ace2 |
|
|
|
b7ace2 |
#ifdef __cplusplus
|
|
|
b7ace2 |
diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c
|
|
|
b7ace2 |
index 98e6038a..5b5d4996 100644
|
|
|
b7ace2 |
--- a/src/libopensc/ctx.c
|
|
|
b7ace2 |
+++ b/src/libopensc/ctx.c
|
|
|
b7ace2 |
@@ -146,6 +146,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = {
|
|
|
b7ace2 |
{ "openpgp", (void *(*)(void)) sc_get_openpgp_driver },
|
|
|
b7ace2 |
{ "jpki", (void *(*)(void)) sc_get_jpki_driver },
|
|
|
b7ace2 |
{ "npa", (void *(*)(void)) sc_get_npa_driver },
|
|
|
b7ace2 |
+ { "cac1", (void *(*)(void)) sc_get_cac1_driver },
|
|
|
b7ace2 |
/* The default driver should be last, as it handles all the
|
|
|
b7ace2 |
* unrecognized cards. */
|
|
|
b7ace2 |
{ "default", (void *(*)(void)) sc_get_default_driver },
|