diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 9b89cc1a..08b38256 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -40,12 +40,12 @@ libopensc_la_SOURCES = \ card-oberthur.c card-belpic.c card-atrust-acos.c card-entersafe.c \ card-incrypto34.c card-piv.c card-muscle.c card-acos5.c \ card-asepcos.c card-akis.c card-gemsafeV1.c card-rutoken.c \ - card-rtecp.c card-westcos.c card-myeid.c \ + card-rtecp.c card-westcos.c card-myeid.c card-ias.c\ \ pkcs15-openpgp.c pkcs15-infocamere.c pkcs15-starcert.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-postecert.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ - pkcs15-esinit.c p15emu-westcos.c \ + pkcs15-esinit.c p15emu-westcos.c pkcs15-pteid.c \ compression.c p15card-helper.c \ \ libopensc.exports diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 343bcb35..43f65b1f 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -29,13 +29,13 @@ OBJECTS = \ card-oberthur.obj card-belpic.obj card-atrust-acos.obj card-entersafe.obj \ card-incrypto34.obj card-piv.obj card-muscle.obj card-acos5.obj \ card-asepcos.obj card-akis.obj card-gemsafeV1.obj card-rutoken.obj \ - card-rtecp.obj card-myeid.obj \ + card-rtecp.obj card-myeid.obj card-ias.obj \ \ p15emu-westcos.obj \ pkcs15-openpgp.obj pkcs15-infocamere.obj pkcs15-starcert.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-postecert.obj pkcs15-gemsafeGPK.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ - pkcs15-esinit.obj \ + pkcs15-esinit.obj pkcs15-pteid.c \ compression.obj p15card-helper.obj \ versioninfo.res diff --git a/src/libopensc/card-gemsafeV1.c b/src/libopensc/card-gemsafeV1.c index c562f2de..352150ff 100644 --- a/src/libopensc/card-gemsafeV1.c +++ b/src/libopensc/card-gemsafeV1.c @@ -15,6 +15,7 @@ */ /* Initially written by David Mattes (david.mattes@boeing.com) */ +/* Portuguese eID card support by Joao Poupino (joao.poupino@ist.utl.pt) */ #include "internal.h" #include "cardctl.h" @@ -32,20 +33,27 @@ static struct sc_card_driver gemsafe_drv = { NULL, 0, NULL }; -static const char *gemexpresso_atrs[] = { - /* standard version */ - "3B:7B:94:00:00:80:65:B0:83:01:01:74:83:00:90:00", - "3B:6B:00:00:80:65:B0:83:01:01:74:83:00:90:00", - /* fips 140 version */ - "3B:6B:00:00:80:65:B0:83:01:03:74:83:00:90:00", - /* TODO: add more ATRs */ - "3B:7A:94:00:00:80:65:A2:01:01:01:3D:72:D6:43", - "3B:7D:94:00:00:80:31:80:65:B0:83:01:01:90:83:00:90:00", - NULL +/* Known ATRs */ +static struct sc_atr_table gemsafe_atrs[] = { + /* standard version */ + {"3B:7B:94:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, + {"3B:6B:00:00:80:65:B0:83:01:01:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, + /* fips 140 version */ + {"3B:6B:00:00:80:65:B0:83:01:03:74:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, + /* Undefined */ + {"3B:7A:94:00:00:80:65:A2:01:01:01:3D:72:D6:43", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, + {"3B:7D:94:00:00:80:31:80:65:B0:83:01:01:90:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_GENERIC, 0, NULL}, + /* Portuguese eID cards */ + {"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, + {"3B:7D:95:00:00:80:31:80:65:B0:83:11:C0:A9:83:00:90:00", NULL, NULL, SC_CARD_TYPE_GEMSAFEV1_PTEID, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} }; static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x18, 0x0A, 0x00, 0x00, 0x01, 0x63, 0x42, 0x00}; + +static const u8 gemsafe_pteid_aid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x00, 0x02}; + /* static const u8 gemsafe_def_aid[] = {0xA0, 0x00, 0x00, 0x00, 0x63, 0x50, 0x4B, 0x43, 0x53, 0x2D, 0x31, 0x35}; @@ -113,27 +121,12 @@ static int gp_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len) return SC_SUCCESS; } -static int gemsafe_match_card(struct sc_card *card) +static int gemsafe_match_card(sc_card_t *card) { - int i, match = -1; + int i; - SC_FUNC_CALLED(card->ctx, 1); - - for (i = 0; gemexpresso_atrs[i] != NULL; i++) { - u8 defatr[SC_MAX_ATR_SIZE]; - size_t len = sizeof(defatr); - const char *atrp = gemexpresso_atrs[i]; - - if (sc_hex_to_bin(atrp, defatr, &len)) - continue; - if (len != card->atr_len) - continue; - if (memcmp(card->atr, defatr, len) != 0) - continue; - match = i + 1; - break; - } - if (match == -1) + i = _sc_match_atr(card, gemsafe_atrs, &card->type); + if (i < 0) return 0; return 1; @@ -153,12 +146,17 @@ static int gemsafe_init(struct sc_card *card) if (!exdata) return SC_ERROR_OUT_OF_MEMORY; exdata->aid_len = sizeof(exdata->aid); - /* try to get a AID from the config file */ - r = get_conf_aid(card, exdata->aid, &exdata->aid_len); - if (r < 0) { - /* failed, use default value */ - memcpy(exdata->aid, gemsafe_def_aid, sizeof(gemsafe_def_aid)); - exdata->aid_len = sizeof(gemsafe_def_aid); + if(card->type == SC_CARD_TYPE_GEMSAFEV1_GENERIC) { + /* try to get a AID from the config file */ + r = get_conf_aid(card, exdata->aid, &exdata->aid_len); + if (r < 0) { + /* failed, use default value */ + memcpy(exdata->aid, gemsafe_def_aid, sizeof(gemsafe_def_aid)); + exdata->aid_len = sizeof(gemsafe_def_aid); + } + } else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { + memcpy(exdata->aid, gemsafe_pteid_aid, sizeof(gemsafe_pteid_aid)); + exdata->aid_len = sizeof(gemsafe_pteid_aid); } /* increase lock_count here to prevent sc_unlock to select @@ -343,18 +341,18 @@ static int gemsafe_process_fci(struct sc_card *card, struct sc_file *file, return SC_SUCCESS; } -static u8 gemsafe_flags2algref(const struct sc_security_env *env) +static u8 gemsafe_flags2algref(struct sc_card *card, const struct sc_security_env *env) { u8 ret = 0; if (env->operation == SC_SEC_OPERATION_SIGN) { if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) - ret = 0x12; + ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12; else if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_ISO9796) ret = 0x11; } else if (env->operation == SC_SEC_OPERATION_DECIPHER) { if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) - ret = 0x12; + ret = card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID ? 0x02 : 0x12; } return ret; @@ -388,7 +386,7 @@ static int gemsafe_set_security_env(struct sc_card *card, if (!(se_env.flags & SC_SEC_ENV_ALG_REF_PRESENT)) { /* set the algorithm reference */ - alg_ref = gemsafe_flags2algref(&se_env); + alg_ref = gemsafe_flags2algref(card, &se_env); if (alg_ref) { se_env.algorithm_ref = alg_ref; se_env.flags |= SC_SEC_ENV_ALG_REF_PRESENT; @@ -417,11 +415,16 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, return SC_ERROR_INVALID_ARGUMENTS; } - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0xAC); - apdu.cla |= 0x80; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 256; + /* the Portuguese eID card requires a two-phase exchange */ + if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2A, 0x90, 0xA0); + } else { + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x2A, 0x9E, 0xAC); + apdu.cla |= 0x80; + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + apdu.le = 256; + } /* we sign a digestInfo object => tag 0x90 */ sbuf[0] = 0x90; sbuf[1] = (u8)data_len; @@ -433,6 +436,17 @@ static int gemsafe_compute_signature(struct sc_card *card, const u8 * data, r = sc_transmit_apdu(card, &apdu); SC_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { + /* finalize the exchange */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0x2A, 0x9E, 0x9A); + apdu.le = 128; /* 1024 bit keys */ + apdu.resp = rbuf; + apdu.resplen = sizeof(rbuf); + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if(apdu.sw1 != 0x90 || apdu.sw2 != 0x00) + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } int len = apdu.resplen > outlen ? outlen : apdu.resplen; memcpy(out, apdu.resp, len); @@ -473,6 +487,25 @@ static int gemsafe_decipher(struct sc_card *card, const u8 * crgram, SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); } +static int gemsafe_get_challenge(sc_card_t *card, u8 *rnd, size_t len) +{ + int prev_cla, r; + + prev_cla = card->cla; + if(card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) { + /* Warning: this depends on iso7816_get_challenge not + * changing the value of the card's CLA + */ + card->cla = 0x80; + } + r = iso_ops->get_challenge(card, rnd, len); + /* Restore the CLA value if needed */ + if(card->cla != prev_cla) + card->cla = prev_cla; + + return r; +} + static int gemsafe_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_pin_cmd_data *data) @@ -622,7 +655,7 @@ static struct sc_card_driver *sc_get_driver(void) iso_ops = iso_drv->ops; /* use the standard iso operations as default */ gemsafe_ops = *iso_drv->ops; - /* gemsafe specfic functions */ + /* gemsafe specific functions */ gemsafe_ops.match_card = gemsafe_match_card; gemsafe_ops.init = gemsafe_init; gemsafe_ops.finish = gemsafe_finish; @@ -631,6 +664,7 @@ static struct sc_card_driver *sc_get_driver(void) gemsafe_ops.set_security_env = gemsafe_set_security_env; gemsafe_ops.decipher = gemsafe_decipher; gemsafe_ops.compute_signature = gemsafe_compute_signature; + gemsafe_ops.get_challenge = gemsafe_get_challenge; gemsafe_ops.process_fci = gemsafe_process_fci; gemsafe_ops.pin_cmd = gemsafe_pin_cmd; diff --git a/src/libopensc/card-ias.c b/src/libopensc/card-ias.c new file mode 100644 index 00000000..034d92d0 --- /dev/null +++ b/src/libopensc/card-ias.c @@ -0,0 +1,550 @@ +/* + * Driver for IAS based cards, e.g. Portugal's eID card. + * + * Copyright (C) 2009, Joao Poupino + * + * 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 + * + * Partially based on the ISO7816 driver. + * + * Thanks to Andre Cruz, Jorge Ferreira and Paulo F. Andrade + */ + +#include "internal.h" +#include "cardctl.h" +#include "asn1.h" +#include +#include + +/* Portugal eID uses 1024 bit keys */ +#define PTEID_RSA_KEYSIZE 128 + +#define DRVDATA(card) ((struct ias_priv_data *) ((card)->drv_data)) + +static struct sc_card_operations ias_ops; +static struct sc_card_operations *iso_ops = NULL; + +static struct sc_card_driver ias_drv = { + "IAS", + "ias", + &ias_ops, + NULL, 0, NULL +}; + +/* Known ATRs */ +static struct sc_atr_table ias_atrs[] = { + /* Portugal eID cards */ + {"3B:65:00:00:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, + {"3B:65:00:00:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, + {"3B:95:95:40:FF:D0:00:54:01:31", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, + {"3B:95:95:40:FF:D0:00:54:01:32", NULL, NULL, SC_CARD_TYPE_IAS_PTEID, 0, NULL}, + {NULL, NULL, NULL, 0, 0, NULL} +}; + +/* Known AIDs */ +static const u8 ias_aid_pteid[] = {0x60, 0x46, 0x32, 0xFF, 0x00, 0x01, 0x02}; + +static int ias_select_applet(sc_card_t *card, const u8 *aid, size_t aid_len) +{ + int r; + sc_path_t tpath; + + tpath.type = SC_PATH_TYPE_DF_NAME; + tpath.len = aid_len; + memcpy(tpath.value, aid, aid_len); + r = iso_ops->select_file(card, &tpath, NULL); + if (r != SC_SUCCESS) { + sc_debug(card->ctx, "unable to select applet"); + return r; + } + + return SC_SUCCESS; +} + +static int ias_init(sc_card_t *card) +{ + unsigned long flags; + + assert(card != NULL); + + SC_FUNC_CALLED(card->ctx, 1); + card->name = "IAS"; + card->cla = 0x00; + + /* Card version detection */ + if (card->type == SC_CARD_TYPE_IAS_PTEID) { + int r = ias_select_applet(card, ias_aid_pteid, sizeof(ias_aid_pteid)); + if (r != SC_SUCCESS) + return r; + /* Add other cards if necessary */ + } else { + return SC_ERROR_INTERNAL; + } + + /* Set card capabilities */ + card->caps |= SC_CARD_CAP_RNG; + + /* Set the supported algorithms */ + flags = SC_ALGORITHM_RSA_PAD_PKCS1 | + SC_ALGORITHM_RSA_HASH_NONE; + + /* Only 1024 bit key sizes were tested */ + _sc_card_add_rsa_alg(card, 1024, flags, 0); + + return SC_SUCCESS; +} + +static int ias_finish(sc_card_t *card) +{ + return SC_SUCCESS; +} + +static int ias_match_card(sc_card_t *card) +{ + int i; + + i = _sc_match_atr(card, ias_atrs, &card->type); + if (i < 0) + return 0; + + return 1; +} + +static int ias_build_pin_apdu(sc_card_t *card, + sc_apdu_t *apdu, + struct sc_pin_cmd_data *data) +{ + static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + int r, len, pad, use_pin_pad, ins, p1; + + r = len = pad = use_pin_pad = ins = p1 = 0; + assert(card != NULL); + + switch (data->pin_type) { + case SC_AC_CHV: + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + + if (data->flags & SC_PIN_CMD_USE_PINPAD) + use_pin_pad = 1; + /* "needs-padding" necessary for the PTEID card, + * but not defined in the pin structure + */ + if ((data->flags & SC_PIN_CMD_NEED_PADDING) || + card->type == SC_CARD_TYPE_IAS_PTEID) + pad = 1; + + data->pin1.offset = 5; + + switch (data->cmd) { + case SC_PIN_CMD_VERIFY: + ins = 0x20; + if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0) + return r; + len = r; + break; + case SC_PIN_CMD_CHANGE: + ins = 0x24; + if ((data->flags & SC_PIN_CMD_IMPLICIT_CHANGE) == 0 && + (data->pin1.len != 0 || use_pin_pad)) { + if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0) + return r; + len += r; + } else { + /* implicit test */ + p1 = 1; + } + data->pin2.offset = data->pin1.offset + len; + if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) + return r; + len += r; + break; + case SC_PIN_CMD_UNBLOCK: + ins = 0x2C; + if (data->pin1.len != 0 || use_pin_pad) { + if ( (r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0) + return r; + len += r; + } else { + p1 |= 0x02; + } + if (data->pin2.len != 0 || use_pin_pad) { + data->pin2.offset = data->pin1.offset + len; + if ( (r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0) + return r; + len += r; + } else { + p1 |= 0x01; + } + break; + default: + return SC_ERROR_NOT_SUPPORTED; + } + + sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); + apdu->lc = len; + apdu->datalen = len; + apdu->data = sbuf; + apdu->resplen = 0; + + return SC_SUCCESS; +} + +static int ias_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, + int *tries_left) +{ + int r; + sc_apdu_t local_apdu; + + SC_FUNC_CALLED(card->ctx, 1); + + /* Check if a PIN change operation is being requested, + * as it requires sending two separate APDUs + */ + if (data->cmd == SC_PIN_CMD_CHANGE) { + /* Build a SC_PIN_CMD_VERIFY APDU */ + data->cmd = SC_PIN_CMD_VERIFY; + r = ias_build_pin_apdu(card, &local_apdu, data); + if (r < 0) + return r; + data->apdu = &local_apdu; + r = iso_ops->pin_cmd(card, data, tries_left); + if (r < 0) + return r; + /* Continue processing */ + data->cmd = SC_PIN_CMD_CHANGE; + /* The IAS spec mandates an implicit change PIN operation */ + data->flags |= SC_PIN_CMD_IMPLICIT_CHANGE; + } + + r = ias_build_pin_apdu(card, &local_apdu, data); + if (r < 0) + return r; + data->apdu = &local_apdu; + + return iso_ops->pin_cmd(card, data, tries_left); +} + +static int ias_set_security_env(sc_card_t *card, + const sc_security_env_t *env, int se_num) +{ + int r; + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + + sc_debug(card->ctx, "ias_set_security_env, keyRef = 0x%0x, algo = 0x%0x\n", + *env->key_ref, env->algorithm_flags); + + assert(card != NULL && env != NULL); + + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0x41, 0); + switch (env->operation) { + case SC_SEC_OPERATION_DECIPHER: + apdu.p2 = 0xB8; /* confidentiality template */ + sbuf[0] = 0x95; /* tag for usage qualifier byte */ + sbuf[1] = 0x01; /* tag length */ + sbuf[2] = 0x40; /* data decryption */ + sbuf[3] = 0x84; /* tag for private key reference */ + sbuf[4] = 0x01; /* tag length */ + sbuf[5] = *env->key_ref; /* key reference */ + sbuf[6] = 0x80; /* tag for algorithm reference */ + sbuf[7] = 0x01; /* tag length */ + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + sbuf[8] = 0x1A; /* RSA PKCS#1 with no data formatting */ + else { + sc_debug(card->ctx, "Set Sec Env: unsupported algo 0X%0X\n", + env->algorithm_flags); + return SC_ERROR_INVALID_ARGUMENTS; + } + apdu.lc = 9; + apdu.datalen = 9; + break; + case SC_SEC_OPERATION_SIGN: + apdu.p2 = 0xA4; /* authentication template */ + sbuf[0] = 0x95; /* tag for usage qualifier byte */ + sbuf[1] = 0x01; /* tag length */ + sbuf[2] = 0x40; /* internal authentication */ + sbuf[3] = 0x84; /* tag for private key reference */ + sbuf[4] = 0x01; /* tag length */ + sbuf[5] = *env->key_ref; /* key reference */ + sbuf[6] = 0x80; /* tag for algorithm reference */ + sbuf[7] = 0x01; /* tag length */ + if (env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) + sbuf[8] = 0x02; /* RSA PKCS#1 with no data formatting */ + else { + sc_debug(card->ctx, "Set Sec Env: unsupported algo 0X%0X\n", + env->algorithm_flags); + return SC_ERROR_INVALID_ARGUMENTS; + } + apdu.lc = 9; + apdu.datalen = 9; + break; + default: + return SC_ERROR_INVALID_ARGUMENTS; + } + apdu.le = 0; + apdu.data = sbuf; + apdu.resplen = 0; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "Set Security Env APDU transmit failed"); + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + SC_TEST_RET(card->ctx, r, "Card's Set Security Env command returned error"); + + return r; +} + +static int ias_compute_signature(sc_card_t *card, const u8 * data, + size_t data_len, u8 * out, size_t outlen) +{ + int r; + size_t len = 0; + sc_apdu_t apdu; + u8 sbuf[SC_MAX_APDU_BUFFER_SIZE]; + sc_context_t *ctx = card->ctx; + + SC_FUNC_CALLED(ctx, 1); + + if (data_len > 64) { + sc_debug(ctx, "error: input data too long: %lu bytes\n", data_len); + return SC_ERROR_INVALID_ARGUMENTS; + } + + /* Send the data */ + sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x88, 0x02, 0x00); + memcpy(sbuf, data, data_len); + apdu.data = sbuf; + apdu.lc = data_len; + apdu.datalen = data_len; + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + + /* Get the result */ + if (apdu.sw1 == 0x90 && apdu.sw2 == 0x00) { + len = card->type == SC_CARD_TYPE_IAS_PTEID ? PTEID_RSA_KEYSIZE : outlen; + r = iso_ops->get_response(card, &len, out); + if (r == 0) + SC_FUNC_RETURN(card->ctx, 2, len); + else + SC_FUNC_RETURN(card->ctx, 2, r); + } + + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); +} + +static int ias_select_file(sc_card_t *card, const sc_path_t *in_path, + sc_file_t **file_out) +{ + int r, pathlen, stripped_len, offset; + u8 buf[SC_MAX_APDU_BUFFER_SIZE]; + u8 pathbuf[SC_MAX_PATH_SIZE], *path; + sc_context_t *ctx; + sc_apdu_t apdu, rapdu; + sc_file_t *file; + + r = pathlen = stripped_len = offset = 0; + path = pathbuf; + file = NULL; + + assert(card != NULL && in_path != NULL); + ctx = card->ctx; + + if (in_path->len > SC_MAX_PATH_SIZE) + return SC_ERROR_INVALID_ARGUMENTS; + memcpy(path, in_path->value, in_path->len); + pathlen = in_path->len; + + sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0xA4, 0, 0); + apdu.p2 = 0; /* First record, return FCI */ + + switch (in_path->type) { + case SC_PATH_TYPE_FILE_ID: + apdu.p1 = 2; + if (pathlen != 2) + return SC_ERROR_INVALID_ARGUMENTS; + break; + case SC_PATH_TYPE_DF_NAME: + apdu.p1 = 4; + break; + case SC_PATH_TYPE_PATH: + apdu.p1 = 9; + /* Strip the MF */ + if (pathlen >= 2 && memcmp(path, "\x3f\x00", 2) == 0) { + if (pathlen == 2) { /* Only 3f00 provided */ + apdu.p1 = 0; + break; + } + path += 2; + pathlen -= 2; + } + /* Optimization based on the normal Portuguese eID usage pattern: + * paths with len >= 4 shall be stripped - this avoids unnecessary + * "file not found" errors. Other cards may benefit from this also. + * + * This works perfectly for the Portuguese eID card, but if you + * are adapting this driver to another card, "false positives" may + * occur depending, of course, on the file structure of the card. + * + * Please have this in mind if adapting this driver to another card. + */ + if (pathlen >= 4) { + stripped_len = pathlen - 2; + path += stripped_len; + pathlen = 2; + } else if (pathlen == 2) { + apdu.p1 = 0; + } + break; + case SC_PATH_TYPE_FROM_CURRENT: + apdu.p1 = 9; + break; + case SC_PATH_TYPE_PARENT: + apdu.p1 = 3; + apdu.p2 = 0x0C; + pathlen = 0; + apdu.cse = SC_APDU_CASE_2_SHORT; + break; + default: + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_INVALID_ARGUMENTS); + } + + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + + if (file_out != NULL) { + apdu.resp = buf; + apdu.resplen = sizeof(buf); + apdu.le = 256; + } else { + apdu.p2 = 0x0C; + apdu.cse = (apdu.lc == 0) ? SC_APDU_CASE_1 : SC_APDU_CASE_3_SHORT; + } + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (file_out == NULL) { + if (apdu.sw1 == 0x61) + SC_FUNC_RETURN(card->ctx, 2, 0); + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } + + /* A "file not found" error was received, this can mean two things: + * 1) the file does not exist + * 2) the current DF may be incorrect due to the optimization applied + * earlier. If the path was previously stripped, select the first DF + * and try to re-select the path with the full value. + */ + if (stripped_len > 0 && apdu.sw1 == 0x6A && apdu.sw2 == 0x82) { + sc_file_t *file = NULL; + sc_path_t tpath; + int i; + + /* Restore original path value */ + path -= stripped_len; + pathlen += stripped_len; + + memset(&tpath, 0, sizeof(sc_path_t)); + tpath.type = SC_PATH_TYPE_PATH; + tpath.len = 2; + if(path[0] == 0x3f && path[1] == 0x00) + offset = 2; + else + offset = 0; + tpath.value[0] = path[offset]; + tpath.value[1] = path[offset + 1]; + + /* Go up in the hierarchy to the correct DF */ + r = ias_select_file(card, &tpath, &file); + SC_TEST_RET(card->ctx, r, "Error selecting parent."); + if(file->type != SC_FILE_TYPE_DF) + return SC_ERROR_FILE_NOT_FOUND; + + /* We're now in the right place, reconstruct the APDU and retry */ + path += offset + 2; + pathlen -= offset + 2; + apdu.lc = pathlen; + apdu.data = path; + apdu.datalen = pathlen; + + if (file_out != NULL) + apdu.resplen = sizeof(buf); + + r = sc_transmit_apdu(card, &apdu); + SC_TEST_RET(card->ctx, r, "APDU transmit failed"); + if (file_out == NULL) { + if (apdu.sw1 == 0x61) + SC_FUNC_RETURN(card->ctx, 2, 0); + SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2)); + } + } + + r = sc_check_sw(card, apdu.sw1, apdu.sw2); + if (r) + SC_FUNC_RETURN(card->ctx, 2, r); + + if (apdu.resplen < 2) + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); + switch (apdu.resp[0]) { + case 0x6F: + file = sc_file_new(); + if (file == NULL) + SC_FUNC_RETURN(card->ctx, 0, SC_ERROR_OUT_OF_MEMORY); + file->path = *in_path; + if (card->ops->process_fci == NULL) { + sc_file_free(file); + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED); + } + if ((size_t)apdu.resp[1] + 2 <= apdu.resplen) + card->ops->process_fci(card, file, apdu.resp+2, apdu.resp[1]); + *file_out = file; + break; + case 0x00: /* proprietary coding */ + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); + default: + SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_UNKNOWN_DATA_RECEIVED); + } + + return SC_SUCCESS; +} + +static struct sc_card_driver *sc_get_driver(void) +{ + struct sc_card_driver *iso_drv = sc_get_iso7816_driver(); + + if (iso_ops == NULL) + iso_ops = iso_drv->ops; + /* Use the standard iso operations as default */ + ias_ops = *iso_drv->ops; + /* IAS specific functions */ + ias_ops.select_file = ias_select_file; + ias_ops.match_card = ias_match_card; + ias_ops.init = ias_init; + ias_ops.finish = ias_finish; + ias_ops.set_security_env = ias_set_security_env; + ias_ops.compute_signature = ias_compute_signature; + ias_ops.pin_cmd = ias_pin_cmd; + + return &ias_drv; +} + +struct sc_card_driver *sc_get_ias_driver(void) +{ + return sc_get_driver(); +} diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index 05f32e1e..65c2cc20 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -151,6 +151,15 @@ enum { /* MyEID cards */ SC_CARD_TYPE_MYEID_BASE = 20000, SC_CARD_TYPE_MYEID_GENERIC, + + /* GemsafeV1 cards */ + SC_CARD_TYPE_GEMSAFEV1_BASE = 21000, + SC_CARD_TYPE_GEMSAFEV1_GENERIC, + SC_CARD_TYPE_GEMSAFEV1_PTEID, + + /* IAS cards */ + SC_CARD_TYPE_IAS_BASE = 22000, + SC_CARD_TYPE_IAS_PTEID, }; extern sc_card_driver_t *sc_get_default_driver(void); @@ -181,6 +190,7 @@ extern sc_card_driver_t *sc_get_rutoken_driver(void); extern sc_card_driver_t *sc_get_rtecp_driver(void); extern sc_card_driver_t *sc_get_westcos_driver(void); extern sc_card_driver_t *sc_get_myeid_driver(void); +extern sc_card_driver_t *sc_get_ias_driver(void); #ifdef __cplusplus } diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index e9832b17..a83c8a83 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -71,6 +71,7 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "oberthur", (void *(*)(void)) sc_get_oberthur_driver }, #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, + { "ias", (void *(*)(void)) sc_get_ias_driver }, { "atrust-acos",(void *(*)(void)) sc_get_atrust_acos_driver }, { "muscle", (void *(*)(void)) sc_get_muscle_driver }, { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, diff --git a/src/libopensc/dir.c b/src/libopensc/dir.c index 634e6e95..b09e6e9c 100644 --- a/src/libopensc/dir.c +++ b/src/libopensc/dir.c @@ -32,7 +32,9 @@ struct app_entry { static const struct app_entry apps[] = { { (const u8 *) "\xA0\x00\x00\x00\x63PKCS-15", 12, "PKCS #15" }, - { (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" }, + { (const u8 *) "\xA0\x00\x00\x01\x77PKCS-15", 12, "Belgian eID" } + /* Needed for the normal PKCS#15 processing of the Portugal eID card */ + /* { (const u8 *) "\x44\x46\x20\x69\x73\x73\x75\x65\x72", 9, "Portugal eID" } */ }; static const struct app_entry * find_app_entry(const u8 * aid, size_t aid_len) diff --git a/src/libopensc/pkcs15-pteid.c b/src/libopensc/pkcs15-pteid.c new file mode 100644 index 00000000..aefb98ac --- /dev/null +++ b/src/libopensc/pkcs15-pteid.c @@ -0,0 +1,295 @@ +/* + * PKCS15 emulation layer for Portugal eID card. + * + * Copyright (C) 2009, Joao Poupino + * Copyright (C) 2004, Martin Paljak + * + * 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 + * + * Based on the PKCS#15 emulation layer for EstEID card by Martin Paljak + * + */ + +/* + * The card has a valid PKCS#15 file system. However, the private keys + * are missing the SC_PKCS15_CO_FLAG_PRIVATE flag and this causes problems + * with some applications (i.e. they don't work). + * + * The three main objectives of the emulation layer are: + * + * 1. Add the necessary SC_PKCS15_CO_FLAG_PRIVATE flag to private keys. + * 2. Hide "superfluous" PKCS#15 objects, e.g. PUKs (the user can't use them). + * 3. Improve usability by providing more descriptive names for the PINs, Keys, etc. + * + */ + +#include "internal.h" +#include "pkcs15.h" +#include +#include +#include + +#include + +#define IAS_CARD 0 +#define GEMSAFE_CARD 1 + +int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); + +static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) +{ + int r, i, type; + unsigned char *buf; + size_t len; + sc_pkcs15_tokeninfo_t tokeninfo; + sc_path_t tmppath; + sc_card_t *card = p15card->card; + sc_context_t *ctx = card->ctx; + + /* Parse the TokenInfo EF */ + sc_format_path("3f004f005032", &tmppath); + r = sc_select_file(card, &tmppath, &p15card->file_tokeninfo); + if (r) + goto end; + if ( (len = p15card->file_tokeninfo->size) == 0) { + sc_error(card->ctx, "EF(TokenInfo) is empty\n"); + goto end; + } + buf = malloc(len); + if (buf == NULL) + return SC_ERROR_OUT_OF_MEMORY; + r = sc_read_binary(card, 0, buf, len, 0); + if (r < 0) + goto end; + if (r <= 2) { + r = SC_ERROR_PKCS15_APP_NOT_FOUND; + goto end; + } + memset(&tokeninfo, 0, sizeof(tokeninfo)); + r = sc_pkcs15_parse_tokeninfo(ctx, &tokeninfo, buf, (size_t) r); + if (r != SC_SUCCESS) + goto end; + p15card->version = tokeninfo.version; + p15card->label = tokeninfo.label; + p15card->serial_number = tokeninfo.serial_number; + p15card->manufacturer_id = tokeninfo.manufacturer_id; + p15card->last_update = tokeninfo.last_update; + p15card->flags = tokeninfo.flags; + p15card->preferred_language = tokeninfo.preferred_language; + p15card->seInfo = tokeninfo.seInfo; + p15card->num_seInfo = tokeninfo.num_seInfo; + + /* Card type detection */ + if (card->type == SC_CARD_TYPE_IAS_PTEID) + type = IAS_CARD; + else if (card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) + type = GEMSAFE_CARD; + else { + r = SC_ERROR_INTERNAL; + goto end; + } + + p15card->flags = SC_PKCS15_CARD_FLAG_PRN_GENERATION + | SC_PKCS15_CARD_FLAG_EID_COMPLIANT + | SC_PKCS15_CARD_FLAG_READONLY; + + /* TODO: Use the cardholder's name? */ + /* TODO: Use Portuguese descriptions? */ + + /* Add X.509 Certificates */ + static const char *pteid_cert_names[4] = { + "AUTHENTICATION CERTIFICATE", + "SIGNATURE CERTIFICATE", + "SIGNATURE SUB CA", + "AUTHENTICATION SUB CA" + }; + /* X.509 Certificate Paths */ + static const char *pteid_cert_paths[4] = { + "3f005f00ef09", /* Authentication Certificate path */ + "3f005f00ef08", /* Digital Signature Certificate path */ + "3f005f00ef0f", /* Signature sub CA path */ + "3f005f00ef10" /* Authentication sub CA path */ + }; + /* X.509 Certificate IDs */ + static const int pteid_cert_ids[4] = {0x45, 0x46, 0x51, 0x52}; + struct sc_pkcs15_cert_info cert_info; + struct sc_pkcs15_object cert_obj; + + for (i = 0; i < 4; i++) { + memset(&cert_info, 0, sizeof(cert_info)); + memset(&cert_obj, 0, sizeof(cert_obj)); + + cert_info.id.value[0] = pteid_cert_ids[i]; + cert_info.id.len = 1; + sc_format_path(pteid_cert_paths[i], &cert_info.path); + strlcpy(cert_obj.label, pteid_cert_names[i], sizeof(cert_obj.label)); + r = sc_pkcs15emu_add_x509_cert(p15card, &cert_obj, &cert_info); + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto end; + } + } + + /* Add PINs */ + static const char *pteid_pin_names[3] = { + "Auth PIN", + "Sign PIN", + "Address PIN" + }; + /* PIN References */ + static const int pteid_pin_ref[2][3] = { {1, 130, 131}, {129, 130, 131} }; + /* PIN Authentication IDs */ + static const int pteid_pin_authid[3] = {1, 2, 3}; + /* PIN Paths */ + static const char *pteid_pin_paths[2][3] = { {NULL, "3f005f00", NULL}, + {NULL, NULL, NULL} }; + struct sc_pkcs15_pin_info pin_info; + struct sc_pkcs15_object pin_obj; + + for (i = 0; i < 3; i++) { + memset(&pin_info, 0, sizeof(pin_info)); + memset(&pin_obj, 0, sizeof(pin_obj)); + + pin_info.auth_id.len = 1; + pin_info.auth_id.value[0] = pteid_pin_authid[i]; + pin_info.reference = pteid_pin_ref[type][i]; + pin_info.flags = SC_PKCS15_PIN_FLAG_NEEDS_PADDING + | SC_PKCS15_PIN_FLAG_INITIALIZED + | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE; + pin_info.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; + pin_info.min_length = 4; + pin_info.stored_length = 8; + pin_info.max_length = 8; + pin_info.pad_char = type == IAS_CARD ? 0x2F : 0xFF; + pin_info.tries_left = -1; + if (pteid_pin_paths[type][i] != NULL) + sc_format_path(pteid_pin_paths[type][i], &pin_info.path); + strlcpy(pin_obj.label, pteid_pin_names[i], sizeof(pin_obj.label)); + pin_obj.flags = 0; + r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info); + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto end; + } + } + + /* Add Private Keys */ + + /* Key reference */ + static const int pteid_prkey_keyref[2][2] = { {1, 130}, {2, 1} }; + /* RSA Private Key usage */ + static int pteid_prkey_usage[2] = { + SC_PKCS15_PRKEY_USAGE_SIGN, + SC_PKCS15_PRKEY_USAGE_NONREPUDIATION}; + /* RSA Private Key IDs */ + static const int pteid_prkey_ids[2] = {0x45, 0x46}; + static const char *pteid_prkey_names[2] = { + "CITIZEN AUTHENTICATION KEY", + "CITIZEN SIGNATURE KEY"}; + /* RSA Private Key Paths */ + static const char *pteid_prkey_paths[2][2] = { {NULL, "3f005f00"}, {NULL, NULL} }; + struct sc_pkcs15_prkey_info prkey_info; + struct sc_pkcs15_object prkey_obj; + + for (i = 0; i < 2; i++) { + memset(&prkey_info, 0, sizeof(prkey_info)); + memset(&prkey_obj, 0, sizeof(prkey_obj)); + + prkey_info.id.len = 1; + prkey_info.id.value[0] = pteid_prkey_ids[i]; + prkey_info.usage = pteid_prkey_usage[i]; + prkey_info.native = 1; + prkey_info.key_reference = pteid_prkey_keyref[type][i]; + prkey_info.modulus_length = 1024; + if (pteid_prkey_paths[type][i] != NULL) + sc_format_path(pteid_prkey_paths[type][i], &prkey_info.path); + strlcpy(prkey_obj.label, pteid_prkey_names[i], sizeof(prkey_obj.label)); + prkey_obj.auth_id.len = 1; + prkey_obj.auth_id.value[0] = i + 1; + prkey_obj.user_consent = (i == 1) ? 1 : 0; + prkey_obj.flags = SC_PKCS15_CO_FLAG_PRIVATE; + + r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); + if (r < 0) { + r = SC_ERROR_INTERNAL; + goto end; + } + } + + /* Add objects */ + static const char *object_ids[3] = {"1", "2", "3"}; + static const char *object_oids[3] = {"-1", "-1", "-1"}; + static const char *object_labels[3] = {"Citizen Data", + "Citizen Address Data", + "Citizen Notepad"}; + static const char *object_authids[3] = {"3", "3", "1"}; + static const char *object_paths[3] = {"3f005f00ef02", + "3f005f00ef05", + "3f005f00ef07"}; + static const int object_flags[3] = {0, + SC_PKCS15_CO_FLAG_PRIVATE, + SC_PKCS15_CO_FLAG_MODIFIABLE}; + struct sc_pkcs15_data_info obj_info; + struct sc_pkcs15_object obj_obj; + + for (i = 0; i < 3; i++) { + memset(&obj_info, 0, sizeof(obj_info)); + memset(&obj_obj, 0, sizeof(obj_obj)); + + sc_pkcs15_format_id(object_ids[i], &obj_info.id); + sc_format_path(object_paths[i], &obj_info.path); + r = sc_format_oid(&obj_info.app_oid, object_oids[i]); + if (r != SC_SUCCESS) + goto end; + strlcpy(obj_info.app_label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE); + if (object_authids[i] != NULL) + sc_pkcs15_format_id(object_authids[i], &obj_obj.auth_id); + strlcpy(obj_obj.label, object_labels[i], SC_PKCS15_MAX_LABEL_SIZE); + obj_obj.flags = object_flags[i]; + + r = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); + if (r < 0) + goto end; + } +end: + if (buf != NULL) { + free(buf); + buf = NULL; + } + if (r) + return r; + + return SC_SUCCESS; +} + +static int pteid_detect_card(sc_pkcs15_card_t *p15card) +{ + if (p15card->card->type == SC_CARD_TYPE_IAS_PTEID || + p15card->card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) + return SC_SUCCESS; + return SC_ERROR_WRONG_CARD; +} + +int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *p15card, sc_pkcs15emu_opt_t *opts) +{ + if (opts != NULL && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + return sc_pkcs15emu_pteid_init(p15card); + else { + int r = pteid_detect_card(p15card); + if (r) + return SC_ERROR_WRONG_CARD; + return sc_pkcs15emu_pteid_init(p15card); + } +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index 58a2e378..7b5deb32 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -56,6 +56,8 @@ extern int sc_pkcs15emu_tccardos_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t extern int sc_pkcs15emu_entersafe_init_ex(sc_pkcs15_card_t *, sc_pkcs15emu_opt_t *); +extern int sc_pkcs15emu_pteid_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 *); @@ -74,6 +76,7 @@ static struct { { "atrust-acos",sc_pkcs15emu_atrust_acos_init_ex}, { "tccardos", sc_pkcs15emu_tccardos_init_ex }, { "entersafe", sc_pkcs15emu_entersafe_init_ex }, + { "pteid", sc_pkcs15emu_pteid_init_ex }, { NULL, NULL } }; @@ -90,6 +93,8 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) { switch (card->type) { case SC_CARD_TYPE_MCRD_ESTEID: + case SC_CARD_TYPE_IAS_PTEID: + case SC_CARD_TYPE_GEMSAFEV1_PTEID: return 1; default: return 0;