diff --git a/src/libopensc/pkcs15-esteid.c b/src/libopensc/pkcs15-esteid.c new file mode 100644 index 000000000..4a5554afd --- /dev/null +++ b/src/libopensc/pkcs15-esteid.c @@ -0,0 +1,236 @@ +/* + * PKCS15 emulation layer for EstEID card. + * + * Copyright (C) 2004, Martin Paljak + * Copyright (C) 2004, Bud P. Bruegger + * Copyright (C) 2004, Antonino Iacono + * 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 + +#include "esteid.h" + +static void +set_string (char **strp, const char *value) +{ + if (*strp) + free (strp); + *strp = value ? strdup (value) : NULL; +} + + +int +select_esteid_df (sc_card_t * card) +{ + int r; + sc_path_t tmppath; + sc_format_path ("3F00EEEE", &tmppath); + tmppath.type = SC_PATH_TYPE_PATH; + r = sc_select_file (card, &tmppath, NULL); + SC_TEST_RET (card->ctx, r, "esteid select DF failed"); + return r; +} + +int +sc_pkcs15emu_esteid_init (sc_pkcs15_card_t * p15card) +{ + sc_card_t *card = p15card->card; + unsigned char buff[256]; + int r, i, flags; + sc_path_t tmppath; + sc_pkcs15_id_t id; + + set_string (&p15card->label, "EstEID isikutunnistus"); + set_string (&p15card->manufacturer_id, "AS Sertifitseerimiskeskus"); + + select_esteid_df (card); + + /* read the serial (document number) */ + sc_format_path ("5044", &tmppath); + tmppath.type = SC_PATH_TYPE_PATH; + r = sc_select_file (card, &tmppath, NULL); + SC_TEST_RET (card->ctx, r, "select esteid PD failed"); + r = sc_read_record (card, SC_ESTEID_PD_DOCUMENT_NR, buff, 8, + SC_RECORD_BY_REC_NR); + SC_TEST_RET (card->ctx, r, "read document number failed"); + // null-terminate + buff[r] = '\0'; + set_string (&p15card->serial_number, buff); + + p15card->flags = + SC_PKCS15_CARD_FLAG_PRN_GENERATION | + SC_PKCS15_CARD_FLAG_EID_COMPLIANT | SC_PKCS15_CARD_FLAG_READONLY; + + /* EstEEID uses 1024b RSA */ + card->algorithm_count = 0; + flags = SC_ALGORITHM_RSA_PAD_PKCS1; + _sc_card_add_rsa_alg (card, 1024, flags, 0); + + /* add certificates */ + for (i = 0; i < 2; i++) + { + static char *esteid_cert_names[2] = { + "Autentimissertifikaat", + "Allkirjasertifikaat" + }; + static char *esteid_cert_paths[2] = { + "3f00eeeeaace", + "3f00eeeeddce" + }; + static int esteid_cert_ids[2] = { + SC_ESTEID_AUTH, + SC_ESTEID_SIGN + }; + sc_path_t path; + sc_pkcs15_id_t auth_id; + + sc_format_path (esteid_cert_paths[i], &path); + path.type = SC_PATH_TYPE_PATH; + auth_id.value[0] = esteid_cert_ids[i]; + auth_id.len = 1; + + r = sc_pkcs15emu_add_cert (p15card, + SC_PKCS15_TYPE_CERT_X509, 0, + &path, &auth_id, esteid_cert_names[i], 0); + } + + /* the file with key pin info (tries left) */ + sc_format_path ("3f000016", &tmppath); + sc_select_file (card, &tmppath, NULL); + + /* add pins */ + for (i = 0; i < 3; i++) + { + char tries_left; + static char *esteid_pin_names[3] = { + "PIN1 - Autentiseerimine", + "PIN2 - Allkirjastamine", + "PUK" + }; + + static int esteid_pin_min[3] = { + 4, + 5, + 8 + }; + + static int esteid_pin_ref[3] = { + 1, + 2, + 0 + }; + + static int esteid_pin_flags[3] = { + 0, + 0, + SC_PKCS15_PIN_FLAG_UNBLOCKING_PIN + }; + + r = sc_read_record (card, i + 1, buff, 128, SC_RECORD_BY_REC_NR); + tries_left = buff[5]; + + id.len = 1; + id.value[0] = i + 1; + + + + + sc_pkcs15emu_add_pin (p15card, &id, + esteid_pin_names[i], NULL, + esteid_pin_ref[i], + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + esteid_pin_min[i], 12, + esteid_pin_flags[i], tries_left, '\0', 0); + } + + /* add private keys */ + for (i = 0; i < 2; i++) + { + static int prkey_pin[2] = { SC_ESTEID_AUTH, SC_ESTEID_SIGN }; + static int prkey_usage[2] = { + SC_PKCS15_PRKEY_USAGE_ENCRYPT | + SC_PKCS15_PRKEY_USAGE_DECRYPT | + SC_PKCS15_PRKEY_USAGE_SIGN | + SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | + SC_PKCS15_PRKEY_USAGE_WRAP | SC_PKCS15_PRKEY_USAGE_UNWRAP, + + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION + }; + static char *prkey_name[2] = { + "Autentiseerimise v\365ti", + "Allkirjastamise v\365ti" + }; + sc_pkcs15_id_t id, auth_id; + + id.value[0] = prkey_pin[i]; + id.len = 1; + auth_id.value[0] = prkey_pin[i]; + auth_id.len = 1; + + // NULL may be a path.... ? + r = sc_pkcs15emu_add_prkey (p15card, &id, + prkey_name[i], + SC_PKCS15_TYPE_PRKEY_RSA, + 1024, prkey_usage[i], NULL, + i + 1, &auth_id, 0); + } + return 0; +} + +static const char *atr1 = "3B:FE:94:00:FF:80:B1:FA:45:1F:03:45:73:74:45:49:44:20:76:65:72:20:31:2E:30:43"; +static const char *atr2 = "3B:6E:00:FF:45:73:74:45:49:44:20:76:65:72:20:31:2E:30"; + +static int esteid_detect_card(sc_pkcs15_card_t *p15card) +{ + u8 buf[SC_MAX_ATR_SIZE]; + size_t len = sizeof(buf); + sc_card_t *card = p15card->card; + + /* XXX: save type of the micardo card in the card structure */ + if (sc_hex_to_bin(atr1, buf, &len)) + return SC_ERROR_INTERNAL; + if (len == card->atr_len && !memcmp(card->atr, buf, len)) + return SC_SUCCESS; + len = sizeof(buf); + if (sc_hex_to_bin(atr2, buf, &len)) + return SC_ERROR_INTERNAL; + if (len == card->atr_len && !memcmp(card->atr, buf, len)) + return SC_SUCCESS; + + return SC_ERROR_WRONG_CARD; +} + +int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_esteid_init(p15card); + else { + int r = esteid_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_esteid_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-infocamere.c b/src/libopensc/pkcs15-infocamere.c new file mode 100644 index 000000000..f38ffe931 --- /dev/null +++ b/src/libopensc/pkcs15-infocamere.c @@ -0,0 +1,294 @@ +/* + * PKCS15 emulation layer for Infocamere SPK2.3 card. + * To see how this works, run p15dump on your Infocamere SPK2.3 card. + * + * Copyright (C) 2004, Antonino Iacono + * 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 +#include +#include + +static void +set_string(char **strp, const char *value) +{ + if (*strp) + free(strp); + *strp = value? strdup(value) : NULL; +} + +int +sc_pkcs15emu_infocamere_init(sc_pkcs15_card_t *p15card) +{ + + static int prkey_usage = SC_PKCS15_PRKEY_USAGE_NONREPUDIATION; + + static int authprkey_usage = SC_PKCS15_PRKEY_USAGE_SIGN + | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER + | SC_PKCS15_PRKEY_USAGE_ENCRYPT + | SC_PKCS15_PRKEY_USAGE_DECRYPT ; + + sc_card_t *card = p15card->card; + sc_path_t path; + struct sc_file *file; + sc_pkcs15_id_t id, auth_id; + unsigned char buffer[256]; + unsigned char ef_gdo[256]; + unsigned char serial[256]; + unsigned char certlen[2]; + int flags; + int authority; + + const char *label = "User Non-repudiation Certificate"; + const char *calabel = "CA Certificate"; + const char *authlabel = "User Authentication Certificate"; + + const char *infocamere_cert_path = "DF01C000"; + const char *infocamere_auth_cert_path = "11111A02"; + const char *infocamere_cacert_path = "DF01C008"; + + const char *authPIN = "Authentication PIN"; + const char *nonrepPIN = "Non-repudiation PIN"; + + const char *authPRKEY = "Authentication Key"; + const char *nonrepPRKEY = "Non repudiation Key"; + + int r; + size_t len_chn, len_iccsn; + + sc_format_path("3F002F02", &path); + + r = sc_select_file(card, &path, &file); + + if (r < 0 || file->size > 255) + { + /* Not EF.GDO */ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + sc_read_binary(card, 0, ef_gdo, file->size, 0); + + if (ef_gdo[0]!=0x5A || file->size < 3) + { + /* Not EF.GDO */ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + len_iccsn = ef_gdo[1]; + + memcpy(buffer,ef_gdo+2,len_iccsn); + + sc_bin_to_hex(buffer, len_iccsn , serial, sizeof(serial), 0); + + if (file->size < (len_iccsn + 5)) + { + /* Not CHN */ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + if (!(ef_gdo[len_iccsn+2]==0x5F && ef_gdo[len_iccsn+3]==0x20)) + { + /* Not CHN */ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + len_chn = ef_gdo[len_iccsn+4]; + + if (len_chn < 2 || len_chn > 8) + { + /* Length CHN incorrect */ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + if (!(ef_gdo[len_iccsn+5]==0x12 && ef_gdo[len_iccsn+6]==0x02)) + { + /* Not Infocamere SPK2.3 Card*/ + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + set_string(&p15card->serial_number, serial); + set_string(&p15card->label, "Infocamere SPK2.3 Card"); + set_string(&p15card->manufacturer_id, "Infocamere"); + + authority = 0; + + /* Get the non-repudiation certificate length */ + + sc_format_path(infocamere_auth_cert_path, &path); + + if (sc_select_file(card, &path, NULL) < 0) + { + r = SC_ERROR_WRONG_CARD; + goto failed; + } + + sc_read_binary(card, 0, certlen, 2, 0); + + /* Now set the certificate offset/len */ + path.index = 2; + path.count = (certlen[1] << 8) + certlen[0]; + + id.value[0] = 1; + id.len = 1; + + sc_pkcs15emu_add_cert(p15card, + SC_PKCS15_TYPE_CERT_X509, authority, + &path, &id, authlabel, SC_PKCS15_CO_FLAG_MODIFIABLE); + + + /* Get the authentication certificate length */ + + sc_format_path(infocamere_cert_path, &path); + + if (sc_select_file(card, &path, NULL) < 0) + { + r = SC_ERROR_INTERNAL; + goto failed; + } + + sc_read_binary(card, 0, certlen, 2, 0); + + /* Now set the certificate offset/len */ + path.index = 2; + path.count = (certlen[1] << 8) + certlen[0]; + + id.value[0] = 2; + + sc_pkcs15emu_add_cert(p15card, + SC_PKCS15_TYPE_CERT_X509, authority, + &path, &id, label, SC_PKCS15_CO_FLAG_MODIFIABLE); + + + /* Get the CA certificate length */ + + authority = 1; + + sc_format_path(infocamere_cacert_path, &path); + + if (sc_select_file(card, &path, NULL) < 0) + { + r = SC_ERROR_INTERNAL; + goto failed; + } + + sc_read_binary(card, 0, certlen, 2, 0); + + /* Now set the certificate offset/len */ + path.index = 2; + path.count = (certlen[1] << 8) + certlen[0]; + + id.value[0] = 3; + + sc_pkcs15emu_add_cert(p15card, + SC_PKCS15_TYPE_CERT_X509, authority, + &path, &id, calabel, SC_PKCS15_CO_FLAG_MODIFIABLE); + + + flags = SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | + SC_PKCS15_PIN_FLAG_INITIALIZED | + SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + + /* add authntication PIN */ + + sc_format_path("3F001111", &path); + id.value[0] = 1; + + sc_pkcs15emu_add_pin(p15card, &id, + authPIN, &path, 0x95, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 5, 8, flags, 3, 0, + SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); + + /* add authentication private key */ + + auth_id.value[0] = 1; + auth_id.len = 1; + sc_pkcs15emu_add_prkey(p15card, &id, + authPRKEY, + SC_PKCS15_TYPE_PRKEY_RSA, + 1024, authprkey_usage, + &path, 0x9B, + &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); + + /* add non repudiation PIN */ + + sc_format_path("3F00DF01", &path); + id.value[0] = 2; + + sc_pkcs15emu_add_pin(p15card, &id, + nonrepPIN, &path, 0x99, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 5, 8, flags, -1, 0, + SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE); + + + /* add non repudiation private key */ + + auth_id.value[0] = 2; + sc_pkcs15emu_add_prkey(p15card, &id, + nonrepPRKEY, + SC_PKCS15_TYPE_PRKEY_RSA, + 1024, prkey_usage, + &path, 0x84, + &auth_id, SC_PKCS15_CO_FLAG_PRIVATE); + + + /* return to MF */ + sc_format_path("3F00", &path); + r = sc_select_file(card, &path, NULL); + + return 0; + +failed: + if (r != SC_ERROR_WRONG_CARD) + sc_error(card->ctx, "Failed to initialize Infocamere SPK2.3 emulation: %s\n", sc_strerror(r)); + return r; + +} + +static int infocamere_detect_card(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + + /* check if we have the correct card OS */ + if (strcmp(card->name, "STARCOS SPK 2.3")) + return SC_ERROR_WRONG_CARD; + return SC_SUCCESS; +} + + +int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_infocamere_init(p15card); + else { + int r = infocamere_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_infocamere_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-netkey.c b/src/libopensc/pkcs15-netkey.c new file mode 100644 index 000000000..60076a5d6 --- /dev/null +++ b/src/libopensc/pkcs15-netkey.c @@ -0,0 +1,229 @@ +/* + * PKCS15 emulation layer for Telesec Netkey E4 card. + * + * Copyright (C) 2004, Peter Koch + * Copyright (C) 2004, Antonino Iacono + * 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 +#include +#include + +static void +set_string(char **strp, const char *value) +{ + if (*strp) + free(strp); + *strp = value? strdup(value) : NULL; +} + +int +sc_pkcs15emu_netkey_init(sc_pkcs15_card_t *p15card) { + static struct { + int id; + char *path; + char *label; + unsigned char pinref; + } pinlist[]={ + {1, "DF015080", "lokale PIN0", 0x80}, + {2, "DF015081", "lokale PIN1", 0x81}, + {0} + }; + static struct { + int id, auth_id; + char *path; + char *label; + unsigned char keyref; + int usage; + } keylist[]={ + {1, 2, "DF015331", "Signatur-Schlüssel", 0x80, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}, + {2, 2, "DF015371", "Authentifizierungs-Schlüssel", 0x82, + SC_PKCS15_PRKEY_USAGE_SIGN | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER | SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT}, + {3, 1, "DF0153B1", "Verschlüsselungs-Schlüssel", 0x81, + SC_PKCS15_PRKEY_USAGE_ENCRYPT | SC_PKCS15_PRKEY_USAGE_DECRYPT}, + {0} + }; + static struct { + int id; + char *path; + char *label; + int obj_flags; + } certlist[]={ + {1, "DF01C000", "Telesec Signatur Zertifikat", 0}, + {1, "DF014331", "User Signatur Zertifikat1", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {1, "DF014332", "User Signatur Zertifikat2", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {2, "DF01C100", "Telesec Authentifizierungs Zertifikat", 0}, + {2, "DF014371", "User Authentifizierungs Zertifikat1", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {2, "DF014372", "User Authentifizierungs Zertifikat2", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {3, "DF01C200", "Telesec Verschlüsselungs Zertifikat", 0}, + {3, "DF0143B1", "User Verschlüsselungs Zertifikat1", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {3, "DF0143B2", "User Verschlüsselungs Zertifikat2", + SC_PKCS15_CO_FLAG_MODIFIABLE}, + {0} + }; + + sc_card_t *card = p15card->card; + sc_context_t *ctx = p15card->card->ctx; + sc_path_t path; + struct sc_file *file; + unsigned char ef_gdo[20]; + unsigned char serial[30]; + int i, r; + + /* Netkey cards serial file 2F02 has always length 12 + * format is: 5A 0A XX XX XX XX XX XX XX XX XX X0 + * where XXXXXXXXXXXXXXXXXXX is the 19-digit serial number + */ + + sc_format_path("2F02", &path); + card->ctx->suppress_errors++; + r=sc_select_file(card, &path, &file); + card->ctx->suppress_errors--; + if (r<0 || file->size!=12) { + sc_debug(ctx, "Cannot read 2F02 (r=%d)\n", r); + r = SC_ERROR_WRONG_CARD; + goto failed; + } + sc_read_binary(card, 0, ef_gdo, 12, 0); + if (ef_gdo[0]!=0x5A || ef_gdo[1]!=10) { + r = SC_ERROR_WRONG_CARD; + sc_debug(ctx, "Invalid 2F02 content %02X %02X ...\n", ef_gdo[0], ef_gdo[1]); + goto failed; + } + sc_bin_to_hex(ef_gdo+2, 10 , serial, sizeof(serial), 0); + serial[19]='\0'; + set_string(&p15card->serial_number, serial); + set_string(&p15card->label, "Netkey E4 Card"); + set_string(&p15card->manufacturer_id, "TeleSec"); + + for(i=0; pinlist[i].id; ++i){ + sc_pkcs15_id_t id; + + if (ctx->debug >= 2) sc_debug(ctx, "Netkey: Loading %s: %s\n", pinlist[i].path, pinlist[i].label); + sc_format_path(pinlist[i].path, &path); + + id.value[0]=pinlist[i].id; id.len=1; + + sc_pkcs15emu_add_pin( + p15card, &id, pinlist[i].label, &path, pinlist[i].pinref, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, 6, 16, + SC_PKCS15_PIN_FLAG_CASE_SENSITIVE | SC_PKCS15_PIN_FLAG_INITIALIZED, + 3, 0, + SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE + ); + } + + for(i=0; keylist[i].id; ++i){ + sc_pkcs15_id_t id, auth_id; + + if (ctx->debug >= 2) sc_debug(ctx, "Netkey: Loading %s\n", keylist[i].label); + + id.value[0] = keylist[i].id; id.len=1; + auth_id.value[0] = keylist[i].auth_id; auth_id.len=1; + + sc_format_path(keylist[i].path, &path); + + sc_pkcs15emu_add_prkey( + p15card, &id, keylist[i].label, SC_PKCS15_TYPE_PRKEY_RSA, + 1024, keylist[i].usage, &path, keylist[i].keyref, + &auth_id, SC_PKCS15_CO_FLAG_PRIVATE + ); + } + + for(i=0; certlist[i].id; ++i){ + unsigned char cert[20]; + sc_pkcs15_id_t id; + + if (ctx->debug >= 2) + sc_debug(ctx, "Netkey: Loading %s: %s\n", certlist[i].path, certlist[i].label); + sc_format_path(certlist[i].path, &path); + card->ctx->suppress_errors++; + r = sc_select_file(card, &path, NULL); + card->ctx->suppress_errors--; + if (r < 0) + continue; + + /* read first 20 bytes of certificate, first two bytes + * must be 0x30 0x82, otherwise this is an empty cert-file + * Telesec-Certificates are prefixed by an OID, + * for example 06:03:55:04:24. this must be skipped + */ + sc_read_binary(card, 0, cert, sizeof(cert), 0); + if(cert[0]!=0x30 || cert[1]!=0x82) continue; + if(cert[4]==0x06 && cert[5]<10 && cert[6+cert[5]]==0x30 && cert[7+cert[5]]==0x82){ + path.index=6+cert[5]; + path.count=(cert[8+cert[5]]<<8) + cert[9+cert[5]] + 4; + } else { + path.index=0; + path.count=(cert[2]<<8) + cert[3] + 4; + } + + id.value[0]=certlist[i].id; id.len=1; + + sc_pkcs15emu_add_cert( + p15card, SC_PKCS15_TYPE_CERT_X509, 0, + &path, &id, certlist[i].label, certlist[i].obj_flags); + } + + /* return to MF */ + sc_format_path("3F00", &path); + r = sc_select_file(card, &path, NULL); + +failed: + if (r < 0) + sc_debug(card->ctx, "Failed to initialize TeleSec Netkey E4 emulation: %s\n", sc_strerror(r)); + return r; +} + +static int netkey_detect_card(sc_pkcs15_card_t *p15card) +{ + int r; + sc_path_t path; + sc_card_t *card = p15card->card; + + /* check if we have the correct card OS */ + if (strcmp(card->name, "TCOS")) + return SC_ERROR_WRONG_CARD; + /* check if we have a df01 DF */ + sc_format_path("3F00DF01", &path); + r = sc_select_file(card, &path, NULL); + if (r < 0) + return SC_ERROR_WRONG_CARD; + return SC_SUCCESS; +} + +int sc_pkcs15emu_netkey_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_netkey_init(p15card); + else { + int r = netkey_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_netkey_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-openpgp.c b/src/libopensc/pkcs15-openpgp.c new file mode 100644 index 000000000..d27131218 --- /dev/null +++ b/src/libopensc/pkcs15-openpgp.c @@ -0,0 +1,221 @@ +/* + * PKCS15 emulation layer for OpenPGP card. + * To see how this works, run p15dump on your 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 + +static char * pgp_pin_name[3] = { + "Signature PIN", + "Encryption PIN", + "Admin PIN" + }; +static char * pgp_key_name[3] = { + "Signature key", + "Encryption key", + "Authentication key" + }; +static char * pgp_pubkey_path[3] = { + "B601", + "B801", + "A401" + }; + +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 +read_file(sc_card_t *card, const char *path_name, void *buf, size_t len) +{ + sc_path_t path; + sc_file_t *file; + int r; + + sc_format_path(path_name, &path); + if ((r = sc_select_file(card, &path, &file)) < 0) + return r; + + if (file->size < len) + len = file->size; + return sc_read_binary(card, 0, (u8 *) buf, len, 0); +} + +int +sc_pkcs15emu_openpgp_init(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + char string[256]; + u8 buffer[256]; + size_t length; + int r, i; + + set_string(&p15card->label, "OpenPGP Card"); + set_string(&p15card->manufacturer_id, "OpenPGP project"); + + if ((r = read_file(card, "004f", 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; + + /* Extract preferred language */ + r = read_file(card, "00655f2d", string, sizeof(string)-1); + if (r < 0) + goto failed; + string[r] = '\0'; + set_string(&p15card->preferred_language, string); + + /* 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 + */ + if ((r = read_file(card, "006E007300C4", buffer, sizeof(buffer))) < 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++) { + sc_path_t path; + sc_pkcs15_id_t auth_id; + 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_format_path("3F00", &path); + auth_id.value[0] = i + 1; + auth_id.len = 1; + sc_pkcs15emu_add_pin(p15card, &auth_id, + pgp_pin_name[i], &path, i+1, + SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 0, buffer[1+i], flags, buffer[4+i], 0, + SC_PKCS15_CO_FLAG_MODIFIABLE | + SC_PKCS15_CO_FLAG_PRIVATE); + } + + for (i = 0; i < 3; i++) { + static int prkey_pin[3] = { 1, 2, 2 }; + static int prkey_usage[3] = { + SC_PKCS15_PRKEY_USAGE_SIGN + | SC_PKCS15_PRKEY_USAGE_SIGNRECOVER + | SC_PKCS15_PRKEY_USAGE_NONREPUDIATION, + SC_PKCS15_PRKEY_USAGE_DECRYPT + | SC_PKCS15_PRKEY_USAGE_UNWRAP, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION + }; + sc_pkcs15_id_t id, auth_id; + + id.value[0] = i + 1; + id.len = 1; + auth_id.value[0] = prkey_pin[i]; + auth_id.len = 1; + sc_pkcs15emu_add_prkey(p15card, &id, + pgp_key_name[i], + SC_PKCS15_TYPE_PRKEY_RSA, + 1024, prkey_usage[i], + NULL, i, + &auth_id, SC_PKCS15_CO_FLAG_PRIVATE | + SC_PKCS15_CO_FLAG_MODIFIABLE); + } + + for (i = 0; i < 3; i++) { + static int pubkey_usage[3] = { + SC_PKCS15_PRKEY_USAGE_VERIFY + | SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER, + SC_PKCS15_PRKEY_USAGE_ENCRYPT + | SC_PKCS15_PRKEY_USAGE_WRAP, + SC_PKCS15_PRKEY_USAGE_VERIFY + }; + sc_pkcs15_id_t id, auth_id; + sc_path_t path; + + id.value[0] = i + 1; + id.len = 1; + auth_id.value[0] = 3; + auth_id.len = 1; + sc_format_path(pgp_pubkey_path[i], &path); + sc_pkcs15emu_add_pubkey(p15card, &id, + pgp_key_name[i], + SC_PKCS15_TYPE_PUBKEY_RSA, + 1024, pubkey_usage[i], + &path, 0, &auth_id, SC_PKCS15_CO_FLAG_MODIFIABLE); + } + + return 0; + +failed: sc_error(card->ctx, "Failed to initialize OpenPGP emulation: %s\n", + sc_strerror(r)); + return r; + +} + +static int openpgp_detect_card(sc_pkcs15_card_t *p15card) +{ + return strcmp(p15card->card->name, "OpenPGP"); +} + +int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_openpgp_init(p15card); + else { + int r = openpgp_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_openpgp_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-starcert.c b/src/libopensc/pkcs15-starcert.c new file mode 100644 index 000000000..b3092b6f0 --- /dev/null +++ b/src/libopensc/pkcs15-starcert.c @@ -0,0 +1,255 @@ +/* + * partial PKCS15 emulation for G&D Starcert V2.2 cards + * + * Copyright (C) 2004, Nils + * + * 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 +#include +#include + +#define MANU_ID "Giesecke & Devrient GmbH" +#define STARCERT "StarCertV2201" + +typedef struct cdata_st { + const char *label; + int authority; + const char *path; + const char *id; + int obj_flags; +} cdata; + +const cdata certs[] = { + {"DS certificate", 0, "3F00DF01C000","1", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {"CA certificate", 1, "3F00DF01C008","2", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {"KE certificate", 0, "3F00DF01C200","3", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {"AUT certificate",0, "3F00DF01C500","4", SC_PKCS15_CO_FLAG_MODIFIABLE}, + {NULL, 0, NULL, 0, 0} +}; + +typedef struct pdata_st { + const char *id; + const char *label; + const char *path; + int ref; + int type; + unsigned int maxlen; + unsigned int minlen; + int flags; + int tries_left; + const char pad_char; + int obj_flags; +} pindata; + +const pindata pins[] = { + { "99", "DS pin", "3F00DF01", 0x99, SC_PKCS15_PIN_TYPE_ASCII_NUMERIC, + 8, 8, SC_PKCS15_PIN_FLAG_NEEDS_PADDING | SC_PKCS15_PIN_FLAG_LOCAL, + 3, 0x00, + SC_PKCS15_CO_FLAG_MODIFIABLE | SC_PKCS15_CO_FLAG_PRIVATE }, + { NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0} +}; + +typedef struct prdata_st { + const char *id; + const char *label; + unsigned int modulus_len; + int usage; + const char *path; + int ref; + const char *auth_id; + int obj_flags; +} prdata; + +#define USAGE_NONREP SC_PKCS15_PRKEY_USAGE_NONREPUDIATION +#define USAGE_KE SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ + SC_PKCS15_PRKEY_USAGE_DECRYPT | \ + SC_PKCS15_PRKEY_USAGE_WRAP | \ + SC_PKCS15_PRKEY_USAGE_UNWRAP +#define USAGE_AUT SC_PKCS15_PRKEY_USAGE_ENCRYPT | \ + SC_PKCS15_PRKEY_USAGE_DECRYPT | \ + SC_PKCS15_PRKEY_USAGE_WRAP | \ + SC_PKCS15_PRKEY_USAGE_UNWRAP | \ + SC_PKCS15_PRKEY_USAGE_SIGN + +const prdata prkeys[] = { + { "1", "DS key", 1024, USAGE_NONREP, "3F00DF01", + 0x84, "99", SC_PKCS15_CO_FLAG_PRIVATE}, + { "3", "KE key", 1024, USAGE_KE, "3F00DF01", + 0x85, NULL, SC_PKCS15_CO_FLAG_PRIVATE}, + { "4", "AUT key", 1024, USAGE_AUT, "3F00DF01", + 0x82, NULL, SC_PKCS15_CO_FLAG_PRIVATE}, + { NULL, NULL, 0, 0, NULL, 0, NULL, 0} +}; + +int get_cert_len(sc_card_t *card, sc_path_t *path) +{ + int r; + u8 buf[8]; + struct sc_file *file; + + r = sc_select_file(card, path, &file); + if (r < 0) + return 0; + r = sc_read_binary(card, 0, buf, sizeof(buf), 0); + if (r < 0) + return 0; + if (buf[0] != 0x30 || buf[1] != 0x82) + return 0; + path->index = 0; + path->count = ((((size_t) buf[2]) << 8) | buf[3]) + 4; + return 1; +} + +static int starcert_detect_card(sc_pkcs15_card_t *p15card) +{ + int r; + u8 buf[128]; + sc_path_t path; + sc_card_t *card = p15card->card; + + /* check if we have the correct card OS */ + if (strcmp(card->name, "STARCOS SPK 2.3")) + return SC_ERROR_WRONG_CARD; + /* read EF_Info file */ + sc_format_path("3F00FE13", &path); + card->ctx->suppress_errors++; + r = sc_select_file(card, &path, NULL); + card->ctx->suppress_errors--; + if (r != SC_SUCCESS) + return SC_ERROR_WRONG_CARD; + r = sc_read_binary(card, 0, buf, 64, 0); + if (r != 64) + return SC_ERROR_WRONG_CARD; + if (memcmp(buf + 24, STARCERT, strlen(STARCERT))) + return SC_ERROR_WRONG_CARD; + + return SC_SUCCESS; +} + +int sc_pkcs15emu_starcert_init(sc_pkcs15_card_t *p15card) +{ + int r, i; + struct sc_apdu apdu; + u8 rbuf[SC_MAX_APDU_BUFFER_SIZE], buf[256]; + struct sc_path path; + struct sc_file *file = NULL; + struct sc_card *card = p15card->card; + + /* use Starcos command GET CARD DATA to determine the + * serial number of the card. + */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xf6, 0x00, 0x00); + apdu.cla |= 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + apdu.lc = 0; + apdu.datalen = 0; + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + return SC_ERROR_INTERNAL; + r = sc_bin_to_hex(apdu.resp, apdu.resplen, buf, sizeof(buf), 0); + if (r != SC_SUCCESS) + return SC_ERROR_INTERNAL; + if (p15card->serial_number) + free(p15card->serial_number); + p15card->serial_number = malloc(strlen(buf) + 1); + if (!p15card->serial_number) + return SC_ERROR_INTERNAL; + strcpy(p15card->serial_number, buf); + /* the TokenInfo version number */ + p15card->version = 0; + /* the manufacturer ID, in this case Giesecke & Devrient GmbH */ + if (p15card->manufacturer_id) + free(p15card->manufacturer_id); + p15card->manufacturer_id = malloc(strlen(MANU_ID) + 1); + if (!p15card->manufacturer_id) + return SC_ERROR_INTERNAL; + strcpy(p15card->manufacturer_id, MANU_ID); + + /* set certs */ + for (i = 0; certs[i].label; i++) { + struct sc_pkcs15_id p15Id; + + sc_format_path(certs[i].path, &path); + if (!get_cert_len(card, &path)) + /* skip errors */ + continue; + sc_pkcs15_format_id(certs[i].id, &p15Id); + sc_pkcs15emu_add_cert(p15card, SC_PKCS15_TYPE_CERT_X509, + certs[i].authority, &path, &p15Id, + certs[i].label, certs[i].obj_flags); + } + /* set pins */ + for (i = 0; pins[i].label; i++) { + struct sc_pkcs15_id p15Id; + + sc_format_path(pins[i].path, &path); + sc_pkcs15_format_id(pins[i].id, &p15Id); + sc_pkcs15emu_add_pin(p15card, &p15Id, pins[i].label, + &path, pins[i].ref, pins[i].type, + pins[i].minlen, pins[i].maxlen, pins[i].flags, + pins[i].tries_left, pins[i].pad_char, + pins[i].obj_flags); + } + /* set private keys */ + for (i = 0; prkeys[i].label; i++) { + struct sc_pkcs15_id p15Id, + authId, *pauthId; + sc_format_path(prkeys[i].path, &path); + sc_pkcs15_format_id(prkeys[i].id, &p15Id); + if (prkeys[i].auth_id) { + sc_pkcs15_format_id(prkeys[i].auth_id, &authId); + pauthId = &authId; + } else + pauthId = NULL; + sc_pkcs15emu_add_prkey(p15card, &p15Id, prkeys[i].label, + SC_PKCS15_TYPE_PRKEY_RSA, + prkeys[i].modulus_len, prkeys[i].usage, + &path, prkeys[i].ref, pauthId, + prkeys[i].obj_flags); + } + + /* select the application DF */ + sc_format_path("3F00DF01", &path); + r = sc_select_file(card, &path, &file); + if (r != SC_SUCCESS || !file) + return SC_ERROR_INTERNAL; + /* set the application DF */ + if (p15card->file_app) + free(p15card->file_app); + p15card->file_app = file; + + return SC_SUCCESS; +} + +int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *p15card, + sc_pkcs15emu_opt_t *opts) +{ + + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_starcert_init(p15card); + else { + int r = starcert_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_starcert_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c new file mode 100644 index 000000000..79743d6df --- /dev/null +++ b/src/libopensc/pkcs15-syn.c @@ -0,0 +1,445 @@ +/* + * pkcs15-syn.c: PKCS #15 emulation of non-pkcs15 cards + * + * Copyright (C) 2003 Olaf Kirch + * 2004 Nils Larsch + * + * 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 + +extern int sc_pkcs15emu_openpgp_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_infocamere_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_starcert_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_netkey_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_esteid_init_ex(sc_pkcs15_card_t *, + sc_pkcs15emu_opt_t *); + +static struct { + const char * name; + int (*handler)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +} builtin_emulators[] = { + { "openpgp", sc_pkcs15emu_openpgp_init_ex }, + { "infocamere", sc_pkcs15emu_infocamere_init_ex }, + { "starcert", sc_pkcs15emu_starcert_init_ex }, + { "netkey", sc_pkcs15emu_netkey_init_ex }, + { "esteid", sc_pkcs15emu_esteid_init_ex }, + { NULL } +}; + +static int parse_emu_block(sc_pkcs15_card_t *, scconf_block *); + +static const char *builtin_name = "builtin"; +static const char *func_name = "sc_pkcs15_init_func"; +static const char *exfunc_name = "sc_pkcs15_init_func_ex"; + + +int +sc_pkcs15_bind_synthetic(sc_pkcs15_card_t *p15card) +{ + sc_context_t *ctx = p15card->card->ctx; + scconf_block *conf_block, **blocks, *blk; + sc_pkcs15emu_opt_t opts; + int i, r = SC_ERROR_WRONG_CARD; + + SC_FUNC_CALLED(ctx, 1); + + memset(&opts, 0, sizeof(opts)); + + 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) { + /* no conf file found => try the internal drivers */ + sc_debug(ctx, "no conf file, trying builtin emulators\n"); + for (i = 0; builtin_emulators[i].name; i++) { + sc_debug(ctx, "trying %s\n", builtin_emulators[i].name); + r = builtin_emulators[i].handler(p15card, &opts); + if (r == SC_SUCCESS) + /* we got a hit */ + goto out; + } + } else { + /* we have a conf file => let's use it */ + const scconf_list *list, *item; + /* find out if the internal drivers should be used */ + i = scconf_get_bool(conf_block, "enable_builtin_emulation", 1); + if (i) { + /* get the list of the internal drivers */ + sc_debug(ctx, "use builtin drivers\n"); + list = scconf_find_list(conf_block, "builtin_emulators"); + for (item = list; item; item = item->next) { + /* get through the list of builtin drivers */ + const char *name = item->data; + + sc_debug(ctx, "trying %s\n", name); + for (i = 0; builtin_emulators[i].name; i++) + if (!strcmp(builtin_emulators[i].name, name)) { + r = builtin_emulators[i].handler(p15card, &opts); + if (r == SC_SUCCESS) + goto out; + } + } + } + /* search for 'emulate foo { ... }' entries in the conf file */ + sc_debug(ctx, "searching for 'emulate foo { ... }' blocks\n"); + blocks = scconf_find_blocks(ctx->conf, conf_block, "emulate", NULL); + + for (i = 0; (blk = blocks[i]) != NULL; i++) { + const char *name = blk->name->data; + sc_debug(ctx, "trying %s\n", name); + r = parse_emu_block(p15card, blk); + if (r == SC_SUCCESS) { + free(blocks); + goto out; + } + } + if (blocks) + free(blocks); + } + + /* Total failure */ + return SC_ERROR_WRONG_CARD; + +out: if (r == SC_SUCCESS) { + 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; +} + +static int emu_detect_card(const sc_card_t *card, const scconf_block *blk) +{ + int r = 1, match = 0; + const scconf_list *list, *item; + /* currently only ATR matching is supported (more to follow) */ + + /* check the ATR */ + list = scconf_find_list(blk, "atr"); + if (list) { + for (item = list; item; item = item->next) { + u8 atr[SC_MAX_ATR_SIZE]; + size_t len = sizeof(atr); + + if (!item->data) + /* skip empty data */ + continue; + if (sc_hex_to_bin(item->data, atr, &len) != SC_SUCCESS) + /* ignore errors, try next atr */ + continue; + if (len == card->atr_len && !memcmp(card->atr, atr, len)){ + match = 1; + break; + } + } + if (match) + r = 1; + else + r = 0; + } + + return r; +} + +static int parse_emu_block(sc_pkcs15_card_t *p15card, scconf_block *conf) +{ + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + sc_pkcs15emu_opt_t opts; + void *dll = NULL; + int (*init_func)(sc_pkcs15_card_t *); + int (*init_func_ex)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); + int r; + const char *module_name; + + r = emu_detect_card(card, conf); + if (!r) + return SC_ERROR_WRONG_CARD; + + init_func = NULL; + init_func_ex = NULL; + opts.blk = conf; + opts.flags = SC_PKCS15EMU_FLAGS_NO_CHECK; + + module_name = scconf_get_str(conf, "module", builtin_name); + + if (!strcmp(module_name, "builtin")) { + int i; + + /* This function is built into libopensc itself. + * Look it up in the table of emulators */ + if (!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_ex = builtin_emulators[i].handler; + break; + } + } + } else { + const char *(*get_version)(void); + const char *name = NULL; + void *address; + + 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; + /* try to get version of the driver/api */ + r = sc_module_get_address(ctx, dll, &address, "sc_driver_version"); + if (r < 0) + get_version = NULL; + else + get_version = (const char *(*)())address; + if (!get_version || strcmp(get_version(), "0.9.3") < 0) { + /* no sc_driver_version function => assume old style + * init function (note: this should later give an error + */ + /* get the init function name */ + name = scconf_get_str(conf, "function", func_name); + + r = sc_module_get_address(ctx, dll, &address, name); + if (r == SC_SUCCESS) + init_func = (int (*)(sc_pkcs15_card_t *)) address; + } else { + name = scconf_get_str(conf, "function", exfunc_name); + + r = sc_module_get_address(ctx, dll, &address, name); + if (r == SC_SUCCESS) + init_func_ex = (int (*)(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *)) address; + } + } + /* try to initialize the pkcs15 structures */ + if (init_func_ex) + r = init_func_ex(p15card, &opts); + else if (init_func) + r = init_func(p15card); + else + r = SC_ERROR_WRONG_CARD; + + 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)); + /* clear pkcs15 card */ + sc_pkcs15_card_clear(p15card); + if (dll) + sc_module_close(ctx, dll); + } + + return r; +} + +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("11001101", &file->path); + sc_pkcs15_add_df(p15card, type, &file->path, file); + created++; + } +} + +int +sc_pkcs15emu_add_object(sc_pkcs15_card_t *p15card, int type, + const char *label, void *data, + const sc_pkcs15_id_t *auth_id, int obj_flags) +{ + sc_pkcs15_object_t *obj; + int df_type; + + obj = (sc_pkcs15_object_t *) calloc(1, sizeof(*obj)); + + obj->type = type; + obj->data = data; + + if (label) + strncpy(obj->label, label, sizeof(obj->label)-1); + + obj->flags = obj_flags; + if (auth_id) + obj->auth_id = *auth_id; + + switch (type & SC_PKCS15_TYPE_CLASS_MASK) { + case SC_PKCS15_TYPE_AUTH: + df_type = SC_PKCS15_AODF; + break; + case SC_PKCS15_TYPE_PRKEY: + 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 SC_ERROR_INVALID_ARGUMENTS; + } + + obj->df = sc_pkcs15emu_get_df(p15card, df_type); + sc_pkcs15_add_object(p15card, obj); + + return 0; +} + +int +sc_pkcs15emu_add_pin(sc_pkcs15_card_t *p15card, + const sc_pkcs15_id_t *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, const char pad_char, int obj_flags) +{ + sc_pkcs15_pin_info_t *info; + + info = (sc_pkcs15_pin_info_t *) calloc(1, sizeof(*info)); + info->auth_id = *id; + 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; + info->magic = SC_PKCS15_PIN_MAGIC; + info->pad_char = pad_char; + + if (path) + info->path = *path; + if (type == SC_PKCS15_PIN_TYPE_BCD) + info->stored_length /= 2; + + return sc_pkcs15emu_add_object(p15card, + SC_PKCS15_TYPE_AUTH_PIN, + label, info, NULL, obj_flags); +} + +int +sc_pkcs15emu_add_cert(sc_pkcs15_card_t *p15card, + int type, int authority, + const sc_path_t *path, + const sc_pkcs15_id_t *id, + const char *label, int obj_flags) +{ + /* const char *label = "Certificate"; */ + sc_pkcs15_cert_info_t *info; + info = (sc_pkcs15_cert_info_t *) calloc(1, sizeof(*info)); + info->id = *id; + info->authority = authority; + if (path) + info->path = *path; + + info->path = *path; + + return sc_pkcs15emu_add_object(p15card, type, label, info, NULL, + obj_flags); +} + +int +sc_pkcs15emu_add_prkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_id_t *id, + const char *label, + int type, unsigned int modulus_length, int usage, + const sc_path_t *path, int ref, + const sc_pkcs15_id_t *auth_id, int obj_flags) +{ + sc_pkcs15_prkey_info_t *info; + + info = (sc_pkcs15_prkey_info_t *) calloc(1, sizeof(*info)); + info->id = *id; + info->modulus_length = modulus_length; + info->usage = usage; + info->native = 1; + info->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_LOCAL; + info->key_reference = ref; + + if (path) + info->path = *path; + + return sc_pkcs15emu_add_object(p15card, + type, label, info, auth_id, obj_flags); +} + +int +sc_pkcs15emu_add_pubkey(sc_pkcs15_card_t *p15card, + const sc_pkcs15_id_t *id, + const char *label, int type, + unsigned int modulus_length, int usage, + const sc_path_t *path, int ref, + const sc_pkcs15_id_t *auth_id, int obj_flags) +{ + sc_pkcs15_pubkey_info_t *info; + + info = (sc_pkcs15_pubkey_info_t *) calloc(1, sizeof(*info)); + info->id = *id; + info->modulus_length = modulus_length; + info->usage = usage; + info->access_flags = SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE; + info->key_reference = ref; + + if (path) + info->path = *path; + + return sc_pkcs15emu_add_object(p15card, type, label, info, auth_id, + obj_flags); +}