From 16ec507738b8ede2bc1d1e4f011b93d796f2a52b Mon Sep 17 00:00:00 2001 From: okir Date: Fri, 31 Oct 2003 12:27:14 +0000 Subject: [PATCH] - Rewrote sc_pkcs15_bind_synthetic a little - Started work on pkcs15 emulation for OpenPGP card git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@1584 c6295689-39f2-0310-b995-f0e70906c6a9 --- src/libopensc/Makefile.am | 6 +- src/libopensc/pkcs15-openpgp.c | 312 +++++++++++++++++++++++++++++++++ src/libopensc/pkcs15-syn.c | 209 ++++++++++++++++++++++ 3 files changed, 525 insertions(+), 2 deletions(-) create mode 100644 src/libopensc/pkcs15-openpgp.c create mode 100644 src/libopensc/pkcs15-syn.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 12b58af9..58031098 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -18,7 +18,7 @@ libopensc_la_SOURCES = \ \ pkcs15.c pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \ pkcs15-prkey.c pkcs15-pubkey.c pkcs15-sec.c \ - pkcs15-wrap.c pkcs15-algo.c pkcs15-cache.c \ + pkcs15-wrap.c pkcs15-algo.c pkcs15-cache.c pkcs15-syn.c \ \ emv.c \ \ @@ -27,7 +27,9 @@ libopensc_la_SOURCES = \ \ card-setcos.c card-miocos.c card-flex.c card-gpk.c \ card-etoken.c card-tcos.c card-emv.c card-default.c \ - card-mcrd.c card-starcos.c card-openpgp.c + card-mcrd.c card-starcos.c card-openpgp.c \ + \ + pkcs15-openpgp.c libopensc_la_LDFLAGS = -version-info @OPENSC_LT_CURRENT@:@OPENSC_LT_REVISION@:@OPENSC_LT_AGE@ libopensc_la_LIBADD = @LIBSCCONF@ ../scdl/libscdl.la @LIBDL@ @LIBCRYPTO@ @OPENCT_LIBS@ @LIBPCSC@ diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c new file mode 100644 index 00000000..9d976cdf --- /dev/null +++ b/src/libopensc/pkcs15-openpgp.c @@ -0,0 +1,312 @@ +/* + * PKCS15 emulation layer for OpenPGP card. + * + * Copyright (C) 2003, Olaf Kirch + * + * 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 "internal.h" +#include "pkcs15.h" +#include "asn1.h" +#include +#include +#include +#include + +/* + * Much of this code probably needs to be shared between + * emulators. Will move this to pkcs15-syn.c when needed. + */ +static sc_pkcs15_df_t * +sc_pkcs15emu_get_df(sc_pkcs15_card_t *p15card, int type) +{ + sc_pkcs15_df_t *df; + sc_file_t *file; + int created = 0; + + while (1) { + for (df = p15card->df_list; df; df = df->next) { + if (df->type == type) { + if (created) + df->enumerated = 1; + return df; + } + } + + assert(created == 0); + + file = sc_file_new(); + sc_format_path("DEAD", &file->path); + sc_pkcs15_add_df(p15card, type, &file->path, file); + created++; + } +} + +static sc_pkcs15_object_t * +sc_pkcs15emu_add_object(sc_pkcs15_card_t *p15card, int type, + const char *label, void *data) +{ + sc_pkcs15_object_t *obj; + int df_type; + + obj = calloc(1, sizeof(*obj)); + obj->type = type; + obj->data = data; + if (label) + strncpy(obj->label, label, sizeof(obj->label)-1); + + if (!(p15card->flags & SC_PKCS15_CARD_FLAG_READONLY)) + obj->flags |= SC_PKCS15_CO_FLAG_MODIFIABLE; + + switch (type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_AUTH: + obj->flags |= SC_PKCS15_CO_FLAG_PRIVATE; + df_type = SC_PKCS15_AODF; + break; + case SC_PKCS15_TYPE_PRKEY: + obj->flags |= SC_PKCS15_CO_FLAG_PRIVATE; + df_type = SC_PKCS15_PRKDF; + break; + case SC_PKCS15_TYPE_PUBKEY: + df_type = SC_PKCS15_PUKDF; + break; + case SC_PKCS15_TYPE_CERT: + df_type = SC_PKCS15_CDF; + break; + default: + sc_error(p15card->card->ctx, + "Unknown PKCS15 object type %d\n", type); + return NULL; + } + + obj->df = sc_pkcs15emu_get_df(p15card, df_type); + sc_pkcs15_add_object(p15card, obj); + + return obj; +} + +static int +sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, + unsigned int id, const char *label, + const sc_path_t *path, int ref, int type, + unsigned int min_length, + unsigned int max_length, + int flags, int tries_left) +{ + sc_pkcs15_pin_info_t *info; + + info = (sc_pkcs15_pin_info_t *) calloc(1, sizeof(*info)); + info->auth_id.value[0] = id; + info->auth_id.len = 1; + info->min_length = min_length; + info->max_length = max_length; + info->stored_length = max_length; + info->type = type; + info->reference = ref; + info->flags = flags; + info->tries_left = tries_left; + + if (path) + info->path = *path; + if (type == SC_PKCS15_PIN_TYPE_BCD) + info->stored_length /= 2; + + sc_pkcs15emu_add_object(p15card, SC_PKCS15_TYPE_AUTH_PIN, label, info); + + return 0; +} + +static void +set_string(char **strp, const char *value) +{ + if (*strp) + free(strp); + *strp = value? strdup(value) : NULL; +} + +/* + * This function pretty much follows what find_tlv in the GNUpg + * code does. + */ +static int +get_tlv(sc_context_t *ctx, unsigned int match_tag, + const u8 *in, size_t in_len, + u8 *out, size_t out_len) +{ + const u8 *end = in + in_len; + int r; + + while (in < end) { + unsigned int tag, len; + int composite = 0; + unsigned char c; + + c = *in++; + if (c == 0x00 || c == 0xFF) + continue; + + tag = c; + if (tag & 0x20) + composite = 1; + while ((c & 0x1f) == 0x1f) { + if (in >= end) + goto eoc; + c = *in++; + tag = (tag << 8) | c; + } + + if (in >= end) + goto eoc; + c = *in++; + if (c < 0x80) { + len = c; + } else { + len = 0; + c &= 0x7F; + while (c--) { + if (in >= end) + goto eoc; + len = (len << 8) | *in++; + } + } + + /* Don't search past end of content */ + if (in + len > end) + goto eoc; + + if (tag == match_tag) { + if (len > out_len) + len = out_len; + memcpy(out, in, len); + return len; + } + + /* Recurse into composite object. + * No need for recursion check, as we check the buffer + * length and each recursion consumes at least 2 bytes */ + if (composite) { + r = get_tlv(ctx, match_tag, in, len, out, out_len); + if (r != SC_ERROR_OBJECT_NOT_FOUND) + return r; + } + + in += len; + } + + return SC_ERROR_OBJECT_NOT_FOUND; + +eoc: sc_error(ctx, "Unexpected end of contentsn"); + return SC_ERROR_OBJECT_NOT_VALID; +} + +int +sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + sc_path_t path; + sc_file_t *file; + char string[256]; + u8 buffer[256], value[256]; + size_t length; + int r, i; + + /* Select OpenPGP application. + * We must specify a file, because the card expects a + * case 4 APDU and barfs if it's case 2. + */ + sc_format_path("D276:0001:2401", &path); + path.type = SC_PATH_TYPE_DF_NAME; + if ((r = sc_select_file(card, &path, &file)) < 0) + goto failed; + sc_file_free(file); + + set_string(&p15card->label, "OpenPGP Card"); + set_string(&p15card->manufacturer_id, "OpenPGP project"); + + if ((r = sc_get_data(card, 0x004f, buffer, sizeof(buffer))) < 0) + goto failed; + sc_bin_to_hex(buffer, r, string, sizeof(string), 0); + set_string(&p15card->serial_number, string); + p15card->version = (buffer[6] << 8) | buffer[7]; + + p15card->flags = SC_PKCS15_CARD_FLAG_LOGIN_REQUIRED | + SC_PKCS15_CARD_FLAG_PRN_GENERATION | + SC_PKCS15_CARD_FLAG_EID_COMPLIANT; + + /* Get Card Holder Related Data (0065) */ + if ((r = sc_get_data(card, 0x0065, buffer, sizeof(buffer))) < 0) + goto failed; + length = r; + + /* Extract preferred language */ + r = get_tlv(ctx, 0x5F2D, buffer, length, string, sizeof(string)-1); + if (r > 0) { + string[r] = '\0'; + set_string(&p15card->preferred_language, string); + } else if (r != SC_ERROR_OBJECT_NOT_FOUND) { + goto failed; + } + + /* Get Application Related Data (006E) */ + if ((r = sc_get_data(card, 0x006E, buffer, sizeof(buffer))) < 0) + goto failed; + length = r; + + /* TBD: extract algorithm info */ + + /* Get CHV status bytes: + * 00: ?? + * 01-03: max length of pins 1-3 + * 04-07: tries left for pins 1-3 + */ + r = get_tlv(ctx, 0x00c4, buffer, length, value, sizeof(value)); + if (r < 0) + goto failed; + if (r != 7) { + sc_error(ctx, + "CHV status bytes have unexpected length " + "(expected 7, got %d)\n", r); + return SC_ERROR_OBJECT_NOT_VALID; + } + + for (i = 0; i < 3; i++) { + static char *pin_name[3] = { + "User PIN", + "User PIN 2", + "Admin PIN" + }; + int flags; + + flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | + SC_PKCS15_PIN_FLAG_INITIALIZED | + SC_PKCS15_PIN_FLAG_LOCAL; + if (i == 2) { + flags |= SC_PKCS15_PIN_FLAG_UNBLOCK_DISABLED | + SC_PKCS15_PIN_FLAG_SO_PIN; + } + sc_pkcs15emu_add_pin(p15card, i+1, pin_name[i], NULL, i+1, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 0, value[1+i], flags, value[4+i]); + } + + return 0; + +failed: sc_error(card->ctx, "Failed to initialize OpenPGP emulation: %s\n", + sc_strerror(r)); + return r; + +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c new file mode 100644 index 00000000..48eb605f --- /dev/null +++ b/src/libopensc/pkcs15-syn.c @@ -0,0 +1,209 @@ +/* + * pkcs15-syn.c: PKCS #15 emaulation of non-pkcs15 cards + * + * Copyright (C) 2003 Olaf Kirch + * + * 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 "internal.h" +#include "pkcs15.h" +#include "asn1.h" +#include +#include +#include +#include + +static int sc_pkcs15_bind_emulation(sc_pkcs15_card_t *, const char *, + scconf_block *, int); + +extern int sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *); + +static struct { + const char * name; + int (*handler)(sc_pkcs15_card_t *); +} builtin_emulators[] = { + { "openpgp", sc_pkcs15emu_openpgp_init }, + + { NULL } +}; + + +int +sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *p15card, int check_atr) +{ + sc_context_t *ctx = p15card->card->ctx; + const scconf_list *clist, *tmp; + scconf_block *conf_block, **blocks, *blk; + int i, r; + + SC_FUNC_CALLED(ctx, 1); + + assert(p15card); + + conf_block = NULL; + for (i = 0; ctx->conf_blocks[i] != NULL; i++) { + blocks = scconf_find_blocks(ctx->conf, ctx->conf_blocks[i], + "framework", "pkcs15"); + if (blocks[0] != NULL) + conf_block = blocks[0]; + free(blocks); + } + if (!conf_block) + return SC_ERROR_WRONG_CARD; + + /* Old-style: get the pkcs15_syn libs from the conf file */ + clist = scconf_find_list(conf_block, "pkcs15_syn"); + for (tmp = clist; tmp != NULL; tmp = tmp->next) { + const char *module = tmp->data; + + if (module == NULL) + continue; + r = sc_pkcs15_bind_emulation(p15card, module, NULL, check_atr); + if (r != SC_ERROR_WRONG_CARD) + goto out; + } + + /* New-style: get lib name, function name, ATR list */ + blocks = scconf_find_blocks(ctx->conf, conf_block, "emulate", NULL); + for (i = 0; (blk = blocks[i]) != NULL; i++) { + const char *module; + + module = scconf_get_str(blk, "module", NULL); + if (!module) + continue; + + r = sc_pkcs15_bind_emulation(p15card, module, blk, check_atr); + if (r != SC_ERROR_WRONG_CARD) { + free(blocks); + goto out; + } + } + free(blocks); + + /* Total failure */ + return SC_ERROR_WRONG_CARD; + +out: if (r == SC_SUCCESS) { + /* p15card->flags |= SC_PKCS15_CARD_FLAG_READONLY; */ + p15card->magic = 0x10203040; + } else if (r != SC_ERROR_WRONG_CARD) { + sc_error(ctx, "Failed to load card emulator: %s\n", + sc_strerror(r)); + } + + return r; +} + +int +sc_pkcs15_bind_emulation(sc_pkcs15_card_t *p15card, + const char *module_name, + scconf_block *conf, + int check_atr) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + const scconf_list *list, *item; + void *dll = NULL; + int (*init_func)(sc_pkcs15_card_t *); + int r; + + if (conf && (list = scconf_find_list(conf, "atr"))) { + int match = 0; + + if (!check_atr) + return SC_ERROR_WRONG_CARD; + for (item = list; item; item = item->next) { + u8 atr[SC_MAX_ATR_SIZE]; + size_t len = sizeof(atr); + + if (!item->data) + continue; + if (sc_hex_to_bin(item->data, atr, &len)) + continue; + if (len > card->atr_len + || memcmp(card->atr, atr, len)) + continue; + match = 1; + break; + } + if (!match) + return SC_ERROR_WRONG_CARD; + } else if (!check_atr) { + /* ATR checking required, but no ATR list to match against */ + return SC_ERROR_WRONG_CARD; + } + + init_func = NULL; + if (!strcmp(module_name, "builtin")) { + int i; + + /* This function is built into libopensc itself. + * Look it up in the table of emulators */ + if (conf == NULL || !conf->name) + return SC_ERROR_INTERNAL; + + module_name = conf->name->data; + for (i = 0; builtin_emulators[i].name; i++) { + if (!strcmp(builtin_emulators[i].name, module_name)) { + init_func = builtin_emulators[i].handler; + break; + } + } + } else { + const char *function_name = NULL; + void *address; + + if (ctx->debug >= 4) + sc_debug(ctx, "Loading %s\n", module_name); + + /* try to open dynamic library */ + r = sc_module_open(ctx, &dll, module_name); + if (r != SC_SUCCESS) + return r; + + /* get a handle to the pkcs15 init function + * XXX the init_func should not modify the contents of + * sc_pkcs15_card_t unless the card is really the one + * the driver is intended for -- Nils + */ + if (conf) + function_name = scconf_get_str(conf, "function", NULL); + if (function_name == NULL) + function_name = "sc_pkcs15_init_func"; + + r = sc_module_get_address(ctx, dll, &address, function_name); + if (r != SC_SUCCESS) + return r; + + /* try to initialize synthetic pkcs15 structures */ + init_func = (int (*)(sc_pkcs15_card_t *)) address; + } + + r = init_func(p15card); + if (r >= 0) { + sc_debug(card->ctx, "%s succeeded, card bound\n", + module_name); + p15card->dll_handle = dll; + } else if (ctx->debug >= 4) { + sc_debug(card->ctx, "%s failed: %s\n", + module_name, sc_strerror(r)); + if (dll) + sc_module_close(ctx, dll); + } + + return r; +} +