From dc073114a0a8d2a6a849f365305143061c5e492e Mon Sep 17 00:00:00 2001 From: Nuno Goncalves Date: Sun, 10 Jul 2016 12:52:32 +0200 Subject: [PATCH] pkcs15-pteid: new implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This implementation reads most of the data from the pkcs15 structure on card, so the objects list are greatly reduced. This improves several pending issues: * drop support for IAS card type In accordance to [1] IAS card type is no longer issued since version 004.003.11 (2010-06-15) and as a legal requirement all documents have been destroyed or declared lost. [1] https://www.cartaodecidadao.pt/documentos/DOC_01-DCM-15_V3_CC_Controlo_Versao_2016-01-20.pdf * fix pteid_cert_ids The Signature and Authentication Sub CA certificates ids were wrong. * add objects and fix flags Add Root CA certificate. Add data objects SOD and TRACe Data object 'Citizen Notepad' doesn't require login to be read. Remove flags. * Support PIN max tries and tries left report * Properly report cards with 2048b keys. Suggested-by: João Poupino Suggested-by: André Guerreiro Signed-off-by: Nuno Goncalves -- closes #806 --- src/libopensc/Makefile.am | 2 +- src/libopensc/Makefile.mak | 2 +- src/libopensc/card-ias.c | 533 ----------------------------------- src/libopensc/cards.h | 5 - src/libopensc/ctx.c | 1 - src/libopensc/pkcs15-pteid.c | 455 +++++++++++++++++------------- src/libopensc/pkcs15-syn.c | 1 - 7 files changed, 265 insertions(+), 734 deletions(-) delete mode 100644 src/libopensc/card-ias.c diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 39056ce1..a31d5e6f 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -38,7 +38,7 @@ libopensc_la_SOURCES = \ card-entersafe.c card-epass2003.c card-coolkey.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-ias.c \ + card-rtecp.c card-westcos.c card-myeid.c \ card-itacns.c card-authentic.c \ card-iasecc.c iasecc-sdo.c iasecc-sm.c card-sc-hsm.c \ card-dnie.c cwa14890.c cwa-dnie.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 8c92000f..741be194 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -20,7 +20,7 @@ OBJECTS = \ card-entersafe.obj card-epass2003.obj card-coolkey.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-westcos.obj card-myeid.obj card-ias.obj \ + card-rtecp.obj card-westcos.obj card-myeid.obj \ card-itacns.obj card-authentic.obj \ card-iasecc.obj iasecc-sdo.obj iasecc-sm.obj cwa-dnie.obj cwa14890.obj \ card-sc-hsm.obj card-dnie.obj card-isoApplet.obj pkcs15-coolkey.obj \ diff --git a/src/libopensc/card-ias.c b/src/libopensc/card-ias.c deleted file mode 100644 index 584d5d9d..00000000 --- a/src/libopensc/card-ias.c +++ /dev/null @@ -1,533 +0,0 @@ -/* - * 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 - */ - -#if HAVE_CONFIG_H -#include "config.h" -#endif - -#include -#include - -#include "internal.h" -#include "asn1.h" -#include "cardctl.h" - -#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; - - memset(&tpath, 0, sizeof(sc_path_t)); - - 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, SC_LOG_DEBUG_NORMAL, "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, SC_LOG_DEBUG_VERBOSE); - 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_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; - - len = pad = use_pin_pad = 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, SC_LOG_DEBUG_VERBOSE); - - /* 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, SC_LOG_DEBUG_NORMAL, "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, SC_LOG_DEBUG_NORMAL, "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, SC_LOG_DEBUG_NORMAL, "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, SC_LOG_DEBUG_NORMAL, r, "Set Security Env APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, 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) -{ - sc_apdu_t apdu; - size_t len; - /* - ** XXX: Ensure sufficient space exists for the card's response - ** as the caller's buffer size may not be sufficient - */ - u8 rbuf[SC_MAX_APDU_BUFFER_SIZE]; - sc_context_t *ctx = card->ctx; - - SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); - - if (data_len > 64) { - sc_debug(ctx, SC_LOG_DEBUG_NORMAL, "error: input data too long: %lu bytes\n", data_len); - return SC_ERROR_INVALID_ARGUMENTS; - } - - sc_format_apdu(card, &apdu, SC_APDU_CASE_4_SHORT, 0x88, 0x02, 0x00); - apdu.data = (u8 *) data; - apdu.lc = data_len; - apdu.datalen = data_len; - apdu.resp = rbuf; - apdu.resplen = sizeof(rbuf); - apdu.le = 256; - - LOG_TEST_RET(card->ctx, sc_transmit_apdu(card, &apdu), "APDU transmit failed"); - LOG_TEST_RET(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2), "INTERNAL AUTHENTICATE failed"); - - len = apdu.resplen > outlen ? outlen : apdu.resplen; - memcpy(out, apdu.resp, len); - - LOG_FUNC_RETURN(card->ctx, apdu.resplen); -} - -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; - u8 buf[SC_MAX_APDU_BUFFER_SIZE]; - u8 pathbuf[SC_MAX_PATH_SIZE], *path; - sc_apdu_t apdu; - sc_file_t *file; - - stripped_len = 0; - path = pathbuf; - file = NULL; - - assert(card != NULL && in_path != NULL); - - 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, SC_LOG_DEBUG_VERBOSE, 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, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (file_out == NULL) { - if (apdu.sw1 == 0x61) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 0); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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_path_t tpath; - - /* 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; - tpath.value[0] = path[0]; - tpath.value[1] = path[1]; - - /* Go up in the hierarchy to the correct DF */ - r = ias_select_file(card, &tpath, NULL); - SC_TEST_RET(card->ctx, SC_LOG_DEBUG_NORMAL, r, "Error selecting parent."); - - /* We're now in the right place, reconstruct the APDU and retry */ - path += 2; - pathlen -= 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, SC_LOG_DEBUG_NORMAL, r, "APDU transmit failed"); - if (file_out == NULL) { - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, sc_check_sw(card, apdu.sw1, apdu.sw2)); - } - } - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - if (r) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); - - if (apdu.resplen < 2) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); - switch (apdu.resp[0]) { - case 0x6F: - file = sc_file_new(); - if (file == NULL) - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_NORMAL, SC_ERROR_OUT_OF_MEMORY); - file->path = *in_path; - if (card->ops->process_fci == NULL) { - sc_file_free(file); - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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, SC_LOG_DEBUG_VERBOSE, SC_ERROR_UNKNOWN_DATA_RECEIVED); - default: - SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, 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.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 7ab94bc2..d0ce7887 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -182,10 +182,6 @@ enum { SC_CARD_TYPE_GEMSAFEV1_PTEID, SC_CARD_TYPE_GEMSAFEV1_SEEID, - /* IAS cards */ - SC_CARD_TYPE_IAS_BASE = 22000, - SC_CARD_TYPE_IAS_PTEID, - /* Italian CNS cards */ SC_CARD_TYPE_ITACNS_BASE = 23000, SC_CARD_TYPE_ITACNS_GENERIC, @@ -264,7 +260,6 @@ 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); extern sc_card_driver_t *sc_get_sc_hsm_driver(void); extern sc_card_driver_t *sc_get_itacns_driver(void); extern sc_card_driver_t *sc_get_authentic_driver(void); diff --git a/src/libopensc/ctx.c b/src/libopensc/ctx.c index 0aaad092..2445c4be 100644 --- a/src/libopensc/ctx.c +++ b/src/libopensc/ctx.c @@ -85,7 +85,6 @@ static const struct _sc_driver_entry internal_card_drivers[] = { { "iasecc", (void *(*)(void)) sc_get_iasecc_driver }, #endif { "belpic", (void *(*)(void)) sc_get_belpic_driver }, - { "ias", (void *(*)(void)) sc_get_ias_driver }, { "incrypto34", (void *(*)(void)) sc_get_incrypto34_driver }, { "acos5", (void *(*)(void)) sc_get_acos5_driver }, { "akis", (void *(*)(void)) sc_get_akis_driver }, diff --git a/src/libopensc/pkcs15-pteid.c b/src/libopensc/pkcs15-pteid.c index 68dacac3..0365ba10 100644 --- a/src/libopensc/pkcs15-pteid.c +++ b/src/libopensc/pkcs15-pteid.c @@ -1,6 +1,7 @@ /* * PKCS15 emulation layer for Portugal eID card. * + * Copyright (C) 2016, Nuno Goncalves * Copyright (C) 2009, Joao Poupino * Copyright (C) 2004, Martin Paljak * @@ -47,198 +48,269 @@ #include "internal.h" #include "pkcs15.h" -#define IAS_CARD 0 -#define GEMSAFE_CARD 1 - +static int pteid_detect_card(struct sc_card *card); int sc_pkcs15emu_pteid_init_ex(sc_pkcs15_card_t *, struct sc_aid *, sc_pkcs15emu_opt_t *); +static +int dump_ef(sc_card_t * card, const char *path, u8 * buf, size_t * buf_len) +{ + int rv; + sc_file_t *file = NULL; + sc_path_t scpath; + sc_format_path(path, &scpath); + rv = sc_select_file(card, &scpath, &file); + if (rv < 0) { + if (file) + sc_file_free(file); + return rv; + } + if (file->size > *buf_len) { + sc_file_free(file); + return SC_ERROR_BUFFER_TOO_SMALL; + } + rv = sc_read_binary(card, 0, buf, file->size, 0); + sc_file_free(file); + if (rv < 0) + return rv; + *buf_len = rv; + + return SC_SUCCESS; +} + +static const struct sc_asn1_entry c_asn1_odf[] = { + {"privateKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 0 | SC_ASN1_CONS, 0, NULL, NULL}, + {"publicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 1 | SC_ASN1_CONS, 0, NULL, NULL}, + {"trustedPublicKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 2 | SC_ASN1_CONS, 0, NULL, NULL}, + {"secretKeys", SC_ASN1_STRUCT, SC_ASN1_CTX | 3 | SC_ASN1_CONS, 0, NULL, NULL}, + {"certificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 4 | SC_ASN1_CONS, 0, NULL, NULL}, + {"trustedCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 5 | SC_ASN1_CONS, 0, NULL, NULL}, + {"usefulCertificates", SC_ASN1_STRUCT, SC_ASN1_CTX | 6 | SC_ASN1_CONS, 0, NULL, NULL}, + {"dataObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 7 | SC_ASN1_CONS, 0, NULL, NULL}, + {"authObjects", SC_ASN1_STRUCT, SC_ASN1_CTX | 8 | SC_ASN1_CONS, 0, NULL, NULL}, + {NULL, 0, 0, 0, NULL, NULL} +}; + +static const unsigned int odf_indexes[] = { + SC_PKCS15_PRKDF, //0 + SC_PKCS15_PUKDF, //1 + SC_PKCS15_PUKDF_TRUSTED, //2 + SC_PKCS15_SKDF, //3 + SC_PKCS15_CDF, //4 + SC_PKCS15_CDF_TRUSTED, //5 + SC_PKCS15_CDF_USEFUL, //6 + SC_PKCS15_DODF, //7 + SC_PKCS15_AODF, //8 +}; + +static +int parse_odf(const u8 * buf, size_t buflen, struct sc_pkcs15_card *p15card) +{ + const u8 *p = buf; + size_t left = buflen; + int r, i, type; + sc_path_t path; + struct sc_asn1_entry asn1_obj_or_path[] = { + {"path", SC_ASN1_PATH, SC_ASN1_CONS | SC_ASN1_SEQUENCE, 0, + &path, NULL}, + {NULL, 0, 0, 0, NULL, NULL} + }; + struct sc_asn1_entry asn1_odf[10]; + + sc_path_t path_prefix; + + sc_format_path("3F004F00", &path_prefix); + + sc_copy_asn1_entry(c_asn1_odf, asn1_odf); + for (i = 0; asn1_odf[i].name != NULL; i++) + sc_format_asn1_entry(asn1_odf + i, asn1_obj_or_path, NULL, 0); + while (left > 0) { + r = sc_asn1_decode_choice(p15card->card->ctx, asn1_odf, p, left, + &p, &left); + if (r == SC_ERROR_ASN1_END_OF_CONTENTS) + break; + if (r < 0) + return r; + type = r; + r = sc_pkcs15_make_absolute_path(&path_prefix, &path); + if (r < 0) + return r; + r = sc_pkcs15_add_df(p15card, odf_indexes[type], &path); + if (r) + return r; + } + return 0; +} + static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) { - int r, i, type; - unsigned char *buf = NULL; - size_t len; - sc_pkcs15_tokeninfo_t tokeninfo; - sc_path_t tmppath; - sc_card_t *card = p15card->card; - sc_context_t *ctx = card->ctx; + u8 buf[1024]; + sc_pkcs15_df_t *df; + sc_pkcs15_object_t *p15_obj; + sc_path_t path; + struct sc_file *file = NULL; + size_t len; + int rv; + int i; - /* 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_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EF(TokenInfo) is empty\n"); - goto end; + sc_context_t *ctx = p15card->card->ctx; + LOG_FUNC_CALLED(ctx); + + /* Check for correct card atr */ + if (pteid_detect_card(p15card->card) != SC_SUCCESS) + return SC_ERROR_WRONG_CARD; + + sc_log(p15card->card->ctx, "Selecting application DF"); + sc_format_path("4F00", &path); + rv = sc_select_file(p15card->card, &path, &file); + if (rv != SC_SUCCESS || !file) + return SC_ERROR_INTERNAL; + /* set the application DF */ + if (p15card->file_app) + free(p15card->file_app); + p15card->file_app = file; + + /* Load TokenInfo */ + len = sizeof(buf); + rv = dump_ef(p15card->card, "4F005032", buf, &len); + if (rv != SC_SUCCESS) { + sc_log(ctx, "Reading of EF.TOKENINFO failed: %d", rv); + LOG_FUNC_RETURN(ctx, rv); } - 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->tokeninfo) = tokeninfo; - - /* 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; + memset(p15card->tokeninfo, 0, sizeof(*p15card->tokeninfo)); + rv = sc_pkcs15_parse_tokeninfo(p15card->card->ctx, p15card->tokeninfo, + buf, len); + if (rv != SC_SUCCESS) { + sc_log(ctx, "Decoding of EF.TOKENINFO failed: %d", rv); + LOG_FUNC_RETURN(ctx, rv); } - p15card->tokeninfo->flags = SC_PKCS15_TOKEN_PRN_GENERATION + p15card->tokeninfo->flags |= SC_PKCS15_TOKEN_PRN_GENERATION | SC_PKCS15_TOKEN_EID_COMPLIANT | SC_PKCS15_TOKEN_READONLY; - /* TODO: Use the cardholder's name? */ - /* TODO: Use Portuguese descriptions? */ - - /* Add X.509 Certificates */ - for (i = 0; i < 4; i++) { - 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; - - 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; - } + /* Load ODF */ + len = sizeof(buf); + rv = dump_ef(p15card->card, "4F005031", buf, &len); + if (rv != SC_SUCCESS) { + sc_log(ctx, "Reading of ODF failed: %d", rv); + LOG_FUNC_RETURN(ctx, rv); + } + rv = parse_odf(buf, len, p15card); + if (rv != SC_SUCCESS) { + sc_log(ctx, "Decoding of ODF failed: %d", rv); + LOG_FUNC_RETURN(ctx, rv); } - - /* Add PINs */ - for (i = 0; i < 3; i++) { - 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", "3f005f00"}, - {NULL, NULL, NULL} }; - struct sc_pkcs15_auth_info pin_info; - struct sc_pkcs15_object pin_obj; - memset(&pin_info, 0, sizeof(pin_info)); - memset(&pin_obj, 0, sizeof(pin_obj)); - - pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN; - pin_info.auth_id.len = 1; - pin_info.auth_id.value[0] = pteid_pin_authid[i]; - pin_info.attrs.pin.reference = pteid_pin_ref[type][i]; - pin_info.attrs.pin.flags = SC_PKCS15_PIN_FLAG_NEEDS_PADDING - | SC_PKCS15_PIN_FLAG_INITIALIZED - | SC_PKCS15_PIN_FLAG_CASE_SENSITIVE; - pin_info.attrs.pin.type = SC_PKCS15_PIN_TYPE_ASCII_NUMERIC; - pin_info.attrs.pin.min_length = 4; - pin_info.attrs.pin.stored_length = 8; - pin_info.attrs.pin.max_length = 8; - pin_info.attrs.pin.pad_char = type == IAS_CARD ? 0x2F : 0xFF; - pin_info.tries_left = -1; - pin_info.logged_in = SC_PIN_STATE_UNKNOWN; - 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; + /* Decode EF.PrKDF, EF.PuKDF, EF.CDF and EF.AODF */ + for (df = p15card->df_list; df != NULL; df = df->next) { + if (df->type == SC_PKCS15_PRKDF) { + rv = sc_pkcs15_parse_df(p15card, df); + if (rv != SC_SUCCESS) { + sc_log(ctx, + "Decoding of EF.PrKDF (%s) failed: %d", + sc_print_path(&df->path), rv); + } + } + if (df->type == SC_PKCS15_PUKDF) { + rv = sc_pkcs15_parse_df(p15card, df); + if (rv != SC_SUCCESS) { + sc_log(ctx, + "Decoding of EF.PuKDF (%s) failed: %d", + sc_print_path(&df->path), rv); + } + } + if (df->type == SC_PKCS15_CDF) { + rv = sc_pkcs15_parse_df(p15card, df); + if (rv != SC_SUCCESS) { + sc_log(ctx, + "Decoding of EF.CDF (%s) failed: %d", + sc_print_path(&df->path), rv); + } + } + if (df->type == SC_PKCS15_AODF) { + rv = sc_pkcs15_parse_df(p15card, df); + if (rv != SC_SUCCESS) { + sc_log(ctx, + "Decoding of EF.AODF (%s) failed: %d", + sc_print_path(&df->path), rv); + } } } - /* Add Private Keys */ - for (i = 0; i < 2; i++) { - /* 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; + p15_obj = p15card->obj_list; + while (p15_obj != NULL) { + if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_PRKDF) ) { + struct sc_pkcs15_prkey_info *prkey_info = (sc_pkcs15_prkey_info_t *) p15_obj->data; + prkey_info->access_flags = SC_PKCS15_PRKEY_ACCESS_SENSITIVE + | SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE + | SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE + | SC_PKCS15_PRKEY_ACCESS_LOCAL; + p15_obj->flags = SC_PKCS15_CO_FLAG_PRIVATE; + } - 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; + if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_AODF) ) { + static const char *pteid_pin_names[3] = { + "Auth PIN", + "Sign PIN", + "Address PIN" + }; - r = sc_pkcs15emu_add_rsa_prkey(p15card, &prkey_obj, &prkey_info); - if (r < 0) { - r = SC_ERROR_INTERNAL; - goto end; + struct sc_pin_cmd_data pin_cmd_data; + struct sc_pkcs15_auth_info *pin_info = (sc_pkcs15_auth_info_t *) p15_obj->data; + + strlcpy(p15_obj->label, pteid_pin_names[pin_info->auth_id.value[0]-1], sizeof(p15_obj->label)); + + pin_info->attrs.pin.flags |= SC_PKCS15_PIN_FLAG_NEEDS_PADDING; + pin_info->tries_left = -1; + pin_info->max_tries = 3; + pin_info->auth_method = SC_AC_CHV; + + memset(&pin_cmd_data, 0, sizeof(pin_cmd_data)); + pin_cmd_data.cmd = SC_PIN_CMD_GET_INFO; + pin_cmd_data.pin_type = pin_info->attrs.pin.type; + pin_cmd_data.pin_reference = pin_info->attrs.pin.reference; + rv = sc_pin_cmd(p15card->card, &pin_cmd_data, NULL); + if (rv == SC_SUCCESS) { + pin_info->tries_left = pin_cmd_data.pin1.tries_left; + } + } + /* Remove found public keys as cannot be read_binary()'d */ + if ( p15_obj->df && (p15_obj->df->type == SC_PKCS15_PUKDF) ) { + sc_pkcs15_object_t *puk = p15_obj; + p15_obj = p15_obj->next; + sc_pkcs15_remove_object(p15card, puk); + sc_pkcs15_free_object(puk); + } else { + p15_obj = p15_obj->next; } } - /* Add objects */ - for (i = 0; i < 3; i++) { - static const char *object_ids[3] = {"01", "02", "03"}; - static const char *object_labels[3] = {"Citizen Data", - "Citizen Address Data", - "Citizen Notepad"}; - static const char *object_authids[3] = {NULL, "03", "01"}; - 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}; + /* Add data objects */ + for (i = 0; i < 5; i++) { + static const char *object_ids[5] = {"1", "2", "3", "4", "5"}; + static const char *object_labels[5] = { + "Citizen Data", + "Citizen Address Data", + "Citizen Notepad", + "SOD", + "TRACE", + }; + static const char *object_authids[5] = {NULL, "3", NULL, NULL, NULL}; + static const char *object_paths[5] = { + "3f005f00ef02", + "3f005f00ef05", + "3f005f00ef07", + "3f005f00ef06", + "3F000003", + }; + static const int object_flags[5] = { + 0, + SC_PKCS15_CO_FLAG_PRIVATE, + 0, + 0, + 0, + }; struct sc_pkcs15_data_info obj_info; struct sc_pkcs15_object obj_obj; @@ -253,37 +325,36 @@ static int sc_pkcs15emu_pteid_init(sc_pkcs15_card_t * p15card) 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; + rv = sc_pkcs15emu_object_add(p15card, SC_PKCS15_TYPE_DATA_OBJECT, &obj_obj, &obj_info); + if (rv != SC_SUCCESS){ + sc_log(ctx, "Object add failed: %d", rv); + break; + } } -end: - if (buf != NULL) { - free(buf); - buf = NULL; - } - if (r) - return r; - return SC_SUCCESS; + LOG_FUNC_RETURN(ctx, SC_SUCCESS); } -static int pteid_detect_card(sc_pkcs15_card_t *p15card) +static int pteid_detect_card(struct sc_card *card) { - if (p15card->card->type == SC_CARD_TYPE_IAS_PTEID || - p15card->card->type == SC_CARD_TYPE_GEMSAFEV1_PTEID) + if (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, struct sc_aid *aid, 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); - } + int r=SC_SUCCESS; + sc_context_t *ctx = p15card->card->ctx; + LOG_FUNC_CALLED(ctx); + + /* if no check flag execute unconditionally */ + if (opts && opts->flags & SC_PKCS15EMU_FLAGS_NO_CHECK) + LOG_FUNC_RETURN(ctx, sc_pkcs15emu_pteid_init(p15card)); + /* check for proper card */ + r = pteid_detect_card(p15card->card); + if (r == SC_ERROR_WRONG_CARD) + LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD); + /* ok: initialize and return */ + LOG_FUNC_RETURN(ctx, sc_pkcs15emu_pteid_init(p15card)); } diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index 14f4d2fc..0163f487 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -76,7 +76,6 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) case SC_CARD_TYPE_MCRD_ESTEID_V10: case SC_CARD_TYPE_MCRD_ESTEID_V11: case SC_CARD_TYPE_MCRD_ESTEID_V30: - case SC_CARD_TYPE_IAS_PTEID: case SC_CARD_TYPE_GEMSAFEV1_PTEID: case SC_CARD_TYPE_OPENPGP_V1: case SC_CARD_TYPE_OPENPGP_V2: