diff --git a/npa/opensc.conf.in b/npa/opensc.conf.in index a33cd95..dd1d8ab 100644 --- a/npa/opensc.conf.in +++ b/npa/opensc.conf.in @@ -19,5 +19,23 @@ app default { card_driver npa { # The location of the driver library module = @libdir@/libcardnpa.@DYN_LIB_EXT@; - # } + } + + + # PKCS #15 + framework pkcs15 { + # additional settings per driver + # + # For pkcs15 emulators loaded from an external shared + # library/DLL, you need to specify the path name of the module + # and customize the card_atr example above correctly. + # + emulate npa { + # The location of the driver library + module = @libdir@/libpkcs15npa.@DYN_LIB_EXT@; + + function = sc_pkcs15emu_npa_init_ex; + can = 222222; + } + } } diff --git a/npa/src/Makefile.am b/npa/src/Makefile.am index fe9807a..044280c 100644 --- a/npa/src/Makefile.am +++ b/npa/src/Makefile.am @@ -27,6 +27,11 @@ libcardnpa_la_LIBADD = $(OPENSC_LIBS) libnpa.la libcardnpa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS) libcardnpa_la_LDFLAGS = -no-undefined +libpkcs15npa_la_SOURCES = pkcs15-npa.c +libpkcs15npa_la_LIBADD = $(OPENSC_LIBS) libcardnpa.la libnpa.la +libpkcs15npa_la_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS) +libpkcs15npa_la_LDFLAGS = -no-undefined + npa_tool_SOURCES = npa-tool.c $(BUILT_SOURCES) npa_tool_LDADD = libnpa.la $(OPENSC_LIBS) $(OPENPACE_LIBS) $(OPENSSL_LIBS) npa_tool_CFLAGS = $(OPENSSL_CFLAGS) $(OPENPACE_CFLAGS) $(OPENSC_CFLAGS) @@ -56,7 +61,7 @@ npa-tool.1: npa-tool.ggo bin_PROGRAMS = npa-tool noinst_PROGRAMS = example -lib_LTLIBRARIES = libnpa.la libcardnpa.la +lib_LTLIBRARIES = libnpa.la libcardnpa.la libpkcs15npa.la noinst_HEADERS = \ sslutil.h \ diff --git a/npa/src/pkcs15-npa.c b/npa/src/pkcs15-npa.c new file mode 100644 index 0000000..f68c2b1 --- /dev/null +++ b/npa/src/pkcs15-npa.c @@ -0,0 +1,258 @@ +/* + * pkcs15-npa.c: PKCS#15 emulation for German ID card + * + * Copyright (C) 2014 Frank Morgner + * + * 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 + */ + +#include "libopensc/log.h" +#include "libopensc/opensc.h" +#include "libopensc/pace.h" +#include "libopensc/pkcs15.h" +#include "card-npa.h" +#include +#include +#include + + +static int npa_detect_card(sc_pkcs15_card_t *p15card) +{ + int r = SC_ERROR_WRONG_CARD; + + if (p15card && p15card->card + && (p15card->card->type == SC_CARD_TYPE_NPA + || p15card->card->type == SC_CARD_TYPE_NPA_TEST + || p15card->card->type == SC_CARD_TYPE_NPA_ONLINE)) { + r = SC_SUCCESS; + } + + return r; +} + +static int npa_add_pin(sc_pkcs15_card_t *p15card, + const char *label, int max_tries, + unsigned int flags, size_t min_length, + size_t max_length, unsigned char reference, + unsigned char auth_id, const struct sc_path *path) +{ + struct sc_pkcs15_auth_info pin_info; + struct sc_pkcs15_object pin_obj; + memset(&pin_info, 0, sizeof pin_info); + memset(&pin_obj, 0, sizeof(pin_obj)); + + strncpy(pin_obj.label, label, sizeof pin_obj.label); + pin_obj.label[(sizeof pin_obj.label) - 1]= '\0'; + if (auth_id) { + pin_obj.auth_id.len = sizeof auth_id; + memcpy(&pin_obj.auth_id.value, &auth_id, sizeof auth_id); + } + + pin_info.auth_id.len = sizeof reference; + memcpy(&pin_info.auth_id.value, &reference, sizeof reference); + pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; + pin_info.auth_method = SC_AC_CHV; + pin_info.tries_left = -1; + pin_info.max_tries = max_tries; + if (path) + memcpy(&pin_info.path, path, sizeof *path); + + pin_info.attrs.pin.flags = flags; + pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.attrs.pin.min_length = min_length; + pin_info.attrs.pin.max_length = max_length; + pin_info.attrs.pin.reference = reference; + + return sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); +} + +static int npa_add_pins(sc_pkcs15_card_t *p15card) +{ + int r; + const sc_path_t *mf = sc_get_mf_path(); + sc_path_t df_esign; + + r = sc_path_set(&df_esign, SC_PATH_TYPE_PATH, + df_esign_path, sizeof df_esign_path, 0, 0); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pin(p15card, "MRZ", -1, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_INITIALIZED + | SC_PKCS15_PIN_FLAG_SO_PIN + | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED + | SC_PKCS15_PIN_FLAG_CHANGE_DISABLED, + 90, 90, PACE_PIN_ID_MRZ, 0, mf); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pin(p15card, "CAN", -1, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_INITIALIZED + | SC_PKCS15_PIN_FLAG_SO_PIN + | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED, + 6, 6, PACE_PIN_ID_CAN, 0, mf); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pin(p15card, "eID PIN", 3, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_INITIALIZED, + 5, 6, PACE_PIN_ID_PIN, PACE_PIN_ID_PUK, mf); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pin(p15card, "PUK", -1, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_INITIALIZED + | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN + | SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED + | SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN + | SC_PKCS15_PIN_FLAG_CHANGE_DISABLED, + 10, 10, PACE_PIN_ID_PUK, 0, mf); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pin(p15card, "eSign PIN", 3, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE + | SC_PKCS15_PIN_FLAG_LOCAL + | SC_PKCS15_PIN_FLAG_INTEGRITY_PROTECTED + | SC_PKCS15_PIN_FLAG_INITIALIZED, + 6, 6, NPA_PIN_ID_ESIGN_PIN, PACE_PIN_ID_PUK, &df_esign); + if (r != SC_SUCCESS) + goto err; + +err: + return r; +} + +static const char npa_manufacturer[] = "Bundesdruckerei GmbH"; + +static int npa_add_cardlabels(sc_pkcs15_card_t *p15card) +{ + if (!p15card || !p15card->tokeninfo) + return SC_ERROR_INTERNAL; + + /* manufacturer ID */ + free(p15card->tokeninfo->manufacturer_id); + p15card->tokeninfo->manufacturer_id = strdup(npa_manufacturer); + if (!p15card->tokeninfo->manufacturer_id) + return SC_ERROR_NOT_ENOUGH_MEMORY; + + /* card label */ + free(p15card->tokeninfo->label); + if (p15card->card && p15card->card->name) { + p15card->tokeninfo->label = strdup(p15card->card->name); + if (!p15card->tokeninfo->label) + return SC_ERROR_NOT_ENOUGH_MEMORY; + } else { + p15card->tokeninfo->label = NULL; + } + + return SC_SUCCESS; +} + +static int npa_get_cert(sc_pkcs15_card_t *p15card, const char *can) +{ + struct establish_pace_channel_input pace_input; + struct establish_pace_channel_output pace_output; + int r; + + if (!p15card || !p15card->card || !p15card->card->reader) { + r = SC_ERROR_INTERNAL; + goto err; + } + + if (!(p15card->card->reader->capabilities & SC_READER_CAP_PACE_ESIGN)) { + sc_log(p15card->card->ctx, "No comfort reader (CAT-K) found\n"); + r = SC_ERROR_NOT_SUPPORTED; + goto err; + } + + memset(&pace_input, 0, sizeof pace_input); + memset(&pace_output, 0, sizeof pace_output); + pace_input.chat = esign_chat; + pace_input.chat_length = sizeof esign_chat; + pace_input.pin_id = PACE_PIN_ID_CAN; + if (can) { + pace_input.pin = (const unsigned char *) can; + pace_input.pin_length = strlen(can); + } + npa_get_cache(p15card->card, pace_input.pin_id, &pace_input.pin, + &pace_input.pin_length, &pace_output.ef_cardaccess, + &pace_output.ef_cardaccess_length); + + r = perform_pace(p15card->card, pace_input, &pace_output, EAC_TR_VERSION_2_02); + if (r != SC_SUCCESS) + goto err; + + /* TODO read certificate */ + r = SC_ERROR_OBJECT_NOT_FOUND; + +err: + return r; +} + +int sc_pkcs15emu_npa_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + int r; + const char *can = NULL; + + if (!p15card || !p15card->card) { + r = SC_ERROR_INTERNAL; + goto err; + } + + if (opts && (opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK)) { + /* don't do a card check */ + } else { + r = npa_detect_card(p15card); + if (r != SC_SUCCESS) + goto err; + } + + r = npa_add_cardlabels(p15card); + if (r != SC_SUCCESS) + goto err; + + r = npa_add_pins(p15card); + if (r != SC_SUCCESS) + goto err; + + if (opts) { + can = scconf_get_str(opts->blk, "can", NULL); + npa_set_cache(p15card->card, PACE_PIN_ID_CAN, + (const unsigned char *) can, strlen(can), NULL, 0); + } + + r = npa_get_cert(p15card, can); + if (r != SC_SUCCESS) { + sc_log(p15card->card->ctx, "No certificate found, will continue anyway\n"); + r = SC_SUCCESS; + } + +err: + return r; +} + +const char *sc_driver_version(void) +{ + /** TODO fix the version check in opensc/src/libopensc/pkcs15-syn.c:271 + * here we choose 0.9.3 simply to pass the bogus check */ + static const char version[] = "0.9.3"; + return version; +}