diff --git a/src/libopensc/Makefile.am b/src/libopensc/Makefile.am index 140665c8..9f0dda5e 100644 --- a/src/libopensc/Makefile.am +++ b/src/libopensc/Makefile.am @@ -51,7 +51,7 @@ libopensc_la_SOURCES_BASE = \ card-isoApplet.c card-masktech.c card-gids.c card-jpki.c \ card-npa.c card-esteid2018.c card-idprime.c \ \ - pkcs15-openpgp.c pkcs15-starcert.c \ + pkcs15-openpgp.c pkcs15-starcert.c pkcs15-cardos.c \ pkcs15-tcos.c pkcs15-esteid.c pkcs15-gemsafeGPK.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c pkcs15-piv.c \ pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \ @@ -134,7 +134,7 @@ TIDY_FILES = \ card-isoApplet.c card-masktech.c card-jpki.c \ card-npa.c card-esteid2018.c card-idprime.c \ \ - pkcs15-openpgp.c \ + pkcs15-openpgp.c pkcs15-cardos.c \ pkcs15-tcos.c pkcs15-esteid.c \ pkcs15-actalis.c pkcs15-atrust-acos.c pkcs15-tccardos.c \ pkcs15-cac.c pkcs15-esinit.c pkcs15-westcos.c pkcs15-pteid.c \ diff --git a/src/libopensc/Makefile.mak b/src/libopensc/Makefile.mak index 2e3c30c2..1b0ff45c 100644 --- a/src/libopensc/Makefile.mak +++ b/src/libopensc/Makefile.mak @@ -29,7 +29,7 @@ OBJECTS = \ card-masktech.obj card-gids.obj card-jpki.obj \ card-npa.obj card-esteid2018.obj card-idprime.obj \ \ - pkcs15-openpgp.obj pkcs15-starcert.obj \ + pkcs15-openpgp.obj pkcs15-starcert.obj pkcs15-cardos.obj \ pkcs15-tcos.obj pkcs15-esteid.obj pkcs15-gemsafeGPK.obj \ pkcs15-actalis.obj pkcs15-atrust-acos.obj pkcs15-tccardos.obj pkcs15-piv.obj \ pkcs15-cac.obj pkcs15-esinit.obj pkcs15-westcos.obj pkcs15-pteid.obj pkcs15-din-66291.obj \ diff --git a/src/libopensc/card-cardos.c b/src/libopensc/card-cardos.c index 306822b9..4ef6ead0 100644 --- a/src/libopensc/card-cardos.c +++ b/src/libopensc/card-cardos.c @@ -53,13 +53,42 @@ static const struct sc_atr_table cardos_atrs[] = { /* CardOS v5.0 */ { "3b:d2:18:00:81:31:fe:58:c9:01:14", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, /* CardOS v5.3 */ - { "3b:d2:18:00:81:31:fe:58:c9:02:17", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, - { "3b:d2:18:00:81:31:fe:58:c9:03:16", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_0, 0, NULL}, + { "3b:d2:18:00:81:31:fe:58:c9:02:17", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, + { "3b:d2:18:00:81:31:fe:58:c9:03:16", NULL, NULL, SC_CARD_TYPE_CARDOS_V5_3, 0, NULL}, { NULL, NULL, NULL, 0, 0, NULL } }; -static unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS]; -static unsigned int algorithm_ids_in_tokeninfo_count=0; +/* private data for cardos driver */ +typedef struct cardos_data { + /* constructed internally */ + unsigned int algorithm_ids_in_tokeninfo[SC_MAX_SUPPORTED_ALGORITHMS]; + unsigned int algorithm_ids_in_tokeninfo_count; + unsigned long flags; /* flags used by init to create sc_algorithms */ + unsigned long ec_flags; + unsigned long ext_flags; + int rsa_2048; + const sc_security_env_t * sec_env; +} cardos_data_t; + +/* copied from iso7816.c */ +static void fixup_transceive_length(const struct sc_card *card, + struct sc_apdu *apdu) +{ + if (card == NULL || apdu == NULL) { + return; + } + + if (apdu->lc > sc_get_max_send_size(card)) { + /* The lower layers will automatically do chaining */ + apdu->flags |= SC_APDU_FLAGS_CHAINING; + } + + if (apdu->le > sc_get_max_recv_size(card)) { + /* The lower layers will automatically do a GET RESPONSE, if possible. + * All other workarounds must be carried out by the upper layers. */ + apdu->le = sc_get_max_recv_size(card); + } +} static int cardos_match_card(sc_card_t *card) { @@ -79,6 +108,8 @@ static int cardos_match_card(sc_card_t *card) return 1; if (card->type == SC_CARD_TYPE_CARDOS_V5_0) return 1; + if (card->type == SC_CARD_TYPE_CARDOS_V5_3) + return 1; if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { int rv; sc_apdu_t apdu; @@ -159,42 +190,102 @@ static int cardos_have_2048bit_package(sc_card_t *card) return 0; } + +/* Called from cardos_init for old cards, from cardos_cardctl_parsed_token_info for new cards */ +/* TODO see if works from old cards too */ +static int cardos_add_algs(sc_card_t *card, unsigned long flags, unsigned long ec_flags, unsigned long ext_flags) +{ + + cardos_data_t * priv = (cardos_data_t *)card->drv_data; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + _sc_card_add_rsa_alg(card, 512, flags, 0); + _sc_card_add_rsa_alg(card, 768, flags, 0); + _sc_card_add_rsa_alg(card, 1024, flags, 0); + if (priv->rsa_2048 == 1) { + _sc_card_add_rsa_alg(card, 1280, flags, 0); + _sc_card_add_rsa_alg(card, 1536, flags, 0); + _sc_card_add_rsa_alg(card, 1792, flags, 0); + _sc_card_add_rsa_alg(card, 2048, flags, 0); + } + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + /* Starting with CardOS 5, the card supports PIN query commands */ + card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; + _sc_card_add_rsa_alg(card, 3072, flags, 0); + _sc_card_add_rsa_alg(card, 4096, flags, 0); + } + + /* TODO need to get sizes from supported_algos too */ + if (ec_flags != 0) { + _sc_card_add_ec_alg(card, 256, ec_flags, priv->ext_flags, NULL); + _sc_card_add_ec_alg(card, 384, ec_flags, priv->ext_flags, NULL); + } + + return 0; +} + static int cardos_init(sc_card_t *card) { - unsigned long flags = 0, rsa_2048 = 0; + cardos_data_t * priv = NULL; + unsigned long flags = 0; size_t data_field_length; sc_apdu_t apdu; u8 rbuf[2]; int r; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + priv = calloc(1, sizeof(cardos_data_t)); + if (!priv) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_OUT_OF_MEMORY); + card->drv_data = priv; + card->name = "Atos CardOS"; card->cla = 0x00; - /* Set up algorithm info. */ - flags = 0; - if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { - flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + /* let user override flags and type from opensc.conf */ + /* user can override card->type too.*/ + if (card->flags) { + flags = card->flags; } else { - flags |= SC_ALGORITHM_RSA_RAW - | SC_ALGORITHM_RSA_HASH_NONE - | SC_ALGORITHM_NEED_USAGE - | SC_ALGORITHM_ONBOARD_KEY_GEN; + + /* Set up algorithm info. */ + flags = 0; + if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { + flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + } else if(card->type == SC_CARD_TYPE_CARDOS_V5_3) { + flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_HASH_NONE + | SC_ALGORITHM_ONBOARD_KEY_GEN; + } else { + flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_HASH_NONE + | SC_ALGORITHM_NEED_USAGE + | SC_ALGORITHM_ONBOARD_KEY_GEN; + } } + priv->flags = flags; + if (card->type == SC_CARD_TYPE_CARDOS_M4_2) { r = cardos_have_2048bit_package(card); if (r < 0) return SC_ERROR_INVALID_CARD; if (r == 1) - rsa_2048 = 1; + priv->rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; - } else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 + } else if (card->type == SC_CARD_TYPE_CARDOS_M4_3 || card->type == SC_CARD_TYPE_CARDOS_M4_2B || card->type == SC_CARD_TYPE_CARDOS_M4_2C || card->type == SC_CARD_TYPE_CARDOS_M4_4 - || card->type == SC_CARD_TYPE_CARDOS_V5_0) { - rsa_2048 = 1; + || card->type == SC_CARD_TYPE_CARDOS_V5_0 + || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + priv->rsa_2048 = 1; card->caps |= SC_CARD_CAP_APDU_EXT; + /* TODO check this. EC only if in supported_algo */ + priv->ext_flags = SC_ALGORITHM_EXT_EC_NAMEDCURVE | SC_ALGORITHM_EXT_EC_UNCOMPRESES; } /* probe DATA FIELD LENGTH with GET DATA */ @@ -202,6 +293,7 @@ static int cardos_init(sc_card_t *card) apdu.le = sizeof rbuf; apdu.resp = rbuf; apdu.resplen = sizeof(rbuf); + r = sc_transmit_apdu(card, &apdu); if (r < 0) LOG_TEST_RET(card->ctx, @@ -216,34 +308,99 @@ static int cardos_init(sc_card_t *card) return SC_ERROR_INVALID_CARD; data_field_length = ((rbuf[0] << 8) | rbuf[1]); - /* strip the length of possible Lc and Le bytes */ - if (card->caps & SC_CARD_CAP_APDU_EXT) - card->max_send_size = data_field_length - 6; - else - card->max_send_size = data_field_length - 3; - /* strip the length of SW bytes */ - card->max_recv_size = data_field_length - 2; + /* TODO is this really needed? strip the length of possible Lc and Le bytes */ - _sc_card_add_rsa_alg(card, 512, flags, 0); - _sc_card_add_rsa_alg(card, 768, flags, 0); - _sc_card_add_rsa_alg(card, 1024, flags, 0); - if (rsa_2048 == 1) { - _sc_card_add_rsa_alg(card, 1280, flags, 0); - _sc_card_add_rsa_alg(card, 1536, flags, 0); - _sc_card_add_rsa_alg(card, 1792, flags, 0); - _sc_card_add_rsa_alg(card, 2048, flags, 0); + /* Use Min card sizes and reader too. for V5_3 at least*/ + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "data_field_length:%"SC_FORMAT_LEN_SIZE_T"u " + "card->reader->max_send_size:%"SC_FORMAT_LEN_SIZE_T"u " + "card->reader->max_recv_size:%"SC_FORMAT_LEN_SIZE_T"u %s", + data_field_length, card->reader->max_send_size, card->reader->max_recv_size, + (card->caps & SC_CARD_CAP_APDU_EXT) ? "SC_CARD_CAP_APDU_EXT" : " "); + + if (card->caps & SC_CARD_CAP_APDU_EXT) { + card->max_send_size = data_field_length - 6; +#ifdef _WIN32 + /* Windows does not support PCSC PART_10 and may have forced reader to 255/256 + * https://github.com/OpenSC/OpenSC/commit/eddea6f3c2d3dafc2c09eba6695c745a61b5186f + * may have reset this. if so, will override and force extended + * Most, if not all, cardos cards do extended, but not chaining + */ + if (card->reader->max_send_size == 255 && card->reader->max_recv_size == 256) { + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "reseting reader to use data_field_length"); + card->reader->max_send_size = data_field_length - 6; + card->reader->max_recv_size = data_field_length - 3; + } +#endif + } else + card->max_send_size = data_field_length - 3; + + card->max_send_size = sc_get_max_send_size(card); /* include reader sizes and protocol */ + card->max_recv_size = data_field_length - 2; + card->max_recv_size = sc_get_max_recv_size(card); + } else { + /* old way, disregards reader capabilities */ + if (card->caps & SC_CARD_CAP_APDU_EXT) + card->max_send_size = data_field_length - 6; + else + card->max_send_size = data_field_length - 3; + /* strip the length of SW bytes */ + card->max_recv_size = data_field_length - 2; } - if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { - /* Starting with CardOS 5, the card supports PIN query commands */ - card->caps |= SC_CARD_CAP_ISO7816_PIN_INFO; - _sc_card_add_rsa_alg(card, 3072, flags, 0); - _sc_card_add_rsa_alg(card, 4096, flags, 0); + /*for new cards, wait till after sc_pkcs15_bind_internal reads tokeninfo */ + if (card->type != SC_CARD_TYPE_CARDOS_V5_0 && card->type != SC_CARD_TYPE_CARDOS_V5_3) { + r = cardos_add_algs(card, flags, 0, 0); } return 0; } +static int cardos_pass_algo_flags(sc_card_t *card, struct sc_cardctl_cardos_pass_algo_flags * ptr) +{ + cardos_data_t * priv = (cardos_data_t *)card->drv_data; + int r = 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + switch (ptr->pass) { + case 1: + ptr->card_flags = card->flags; + ptr->used_flags = priv->flags; + ptr->ec_flags = priv->ec_flags; + ptr->ext_flags = priv->ext_flags; + break; + case 2: + r = cardos_add_algs(card,ptr->new_flags, ptr->ec_flags, ptr->ext_flags); + break; + default: + sc_log(card->ctx, "ptr->pass: %ul invalid", ptr->pass); + r = SC_ERROR_INTERNAL; + } + LOG_FUNC_RETURN(card->ctx, r); +} + + +static int cardos_finish(sc_card_t *card) +{ + int r = 0; + + if (card) + return 0; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* free priv data */ + if (card->drv_data) { /* priv */ + free(card->drv_data); + card->drv_data = NULL; + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); +} + + + static const struct sc_card_error cardos_errors[] = { /* some error inside the card */ /* i.e. nothing you can do */ @@ -772,8 +929,9 @@ cardos_set_security_env(sc_card_t *card, const sc_security_env_t *env, int se_num) { + cardos_data_t* priv = (cardos_data_t*)card->drv_data; sc_apdu_t apdu; - u8 data[6]; + u8 data[9]; int key_id, r; assert(card != NULL && env != NULL); @@ -782,6 +940,15 @@ cardos_set_security_env(sc_card_t *card, sc_log(card->ctx, "No or invalid key reference\n"); return SC_ERROR_INVALID_ARGUMENTS; } + priv->sec_env = env; /* pass on to crypto routines */ + + /* key_ref includes card mechanism and key number + * But newer cards appear to get this some other way, + * We can use flags passed to know what OpenSC expects from the card + * and have derived what these machanisums are. + * Newer cards may change how this is done + */ + key_id = env->key_ref[0]; sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x22, 0, 0); @@ -802,16 +969,39 @@ cardos_set_security_env(sc_card_t *card, return SC_ERROR_INVALID_ARGUMENTS; } - if (card->type == SC_CARD_TYPE_CARDOS_V5_0) { + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + /* some cards appear to have key_id be both Cryptographic mechanism reference 4 bits + * and key_ref 4 bits. But this limits card to 16 keys. + * TODO may need to be looked at at a later time + */ /* Private key reference */ data[0] = 0x84; data[1] = 0x01; - data[2] = key_id; + data[2] = key_id & 0x0F; /* Usage qualifier byte */ data[3] = 0x95; data[4] = 0x01; data[5] = 0x40; apdu.lc = apdu.datalen = 6; + if (key_id & 0xF0) { + /* Cryptographic mechanism reference */ + data[6] = 0x80; + data[7] = 0x01; + data[8] = key_id & 0xF0; + apdu.lc = apdu.datalen = 9; + } else if (priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_PAD_PKCS1) { + /* TODO this may only apply to c903 cards */ + /* TODO or only for cards without any supported_algos or EIDComplient only */ + data[6] = 0x80; + data[7] = 0x01; + data[8] = 0x10; + apdu.lc = apdu.datalen = 9; + } else if (priv->sec_env->algorithm_flags & SC_ALGORITHM_ECDSA_RAW) { + data[6] = 0x80; + data[7] = 0x01; + data[8] = 0x30; + apdu.lc = apdu.datalen = 9; + } } else { data[0] = 0x83; data[1] = 0x01; @@ -839,12 +1029,12 @@ cardos_set_security_env(sc_card_t *card, sc_log(card->ctx, "is signature"); sc_log(card->ctx, "Adding ID %d at index %d", algorithm_id, algorithm_id_count); - algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id; + priv->algorithm_ids_in_tokeninfo[algorithm_id_count++] = algorithm_id; } sc_log(card->ctx, "reference=%d, mechanism=%d, operations=%d, algo_ref=%d", alg.reference, alg.mechanism, alg.operations, alg.algo_ref); } - algorithm_ids_in_tokeninfo_count = algorithm_id_count; + priv -> algorithm_ids_in_tokeninfo_count = algorithm_id_count; } while (0); LOG_FUNC_RETURN(card->ctx, r); @@ -859,6 +1049,7 @@ static int do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { + /* cardos_data_t* priv = (cardos_data_t*)card->drv_dataa */; int r; sc_apdu_t apdu; @@ -873,6 +1064,7 @@ do_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, apdu.data = data; apdu.lc = datalen; apdu.datalen = datalen; + fixup_transceive_length(card, &apdu); r = sc_transmit_apdu(card, &apdu); LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); @@ -886,6 +1078,7 @@ static int cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, u8 *out, size_t outlen) { + cardos_data_t* priv; int r; sc_context_t *ctx; int do_rsa_pure_sig = 0; @@ -895,8 +1088,21 @@ cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, assert(card != NULL && data != NULL && out != NULL); ctx = card->ctx; + priv = (cardos_data_t*)card->drv_data; SC_FUNC_CALLED(ctx, SC_LOG_DEBUG_VERBOSE); + /* sec_env has algorithm_flags set from sc_get_encoding_flags sec_flags + * If flags are set correctly we don't need to test anything + * TODO this assumes RSA is PSS, PKCS1 or RAW and we are passing + * the correct data. Should work for ECDSA too. + * use for V5 cards and TODO should for older cards too + */ + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + + r = do_compute_signature(card, data, datalen, out, outlen); + LOG_FUNC_RETURN(ctx, r); + } + /* There are two ways to create a signature, depending on the way, * the key was created: RSA_SIG and RSA_PURE_SIG. * We can use the following reasoning, to determine the correct operation: @@ -913,8 +1119,8 @@ cardos_compute_signature(sc_card_t *card, const u8 *data, size_t datalen, */ /* check the the algorithmIDs from the AlgorithmInfo */ - for (i = 0; i < algorithm_ids_in_tokeninfo_count; ++i) { - unsigned int id = algorithm_ids_in_tokeninfo[i]; + for (i = 0; i < priv->algorithm_ids_in_tokeninfo_count; ++i) { + unsigned int id = priv->algorithm_ids_in_tokeninfo[i]; if (id == 0x86 || id == 0x88) { do_rsa_sig = 1; } else if (id == 0x8C || id == 0x8A) { @@ -985,10 +1191,41 @@ cardos_decipher(struct sc_card *card, const u8 * crgram, size_t crgram_len, u8 * out, size_t outlen) { + cardos_data_t* priv = (cardos_data_t*)card->drv_data; int r; size_t card_max_send_size = card->max_send_size; size_t reader_max_send_size = card->reader->max_send_size; + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + /* 5.3 supports command chaining. Others may also + * card_max_send_size for 5.3 is already based on reader max_send_size */ + + if (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3) { + + r = iso_ops->decipher(card, crgram, crgram_len, out, outlen); + /* + * 5.3 supports RAW as well as PKCS1 and PSS + * decription may strip padding if card supports it + * with cards that support RAW, it always appears to + * drop first 00 that is start of padding. + */ + + if (r > 0 && priv->sec_env->algorithm_flags & SC_ALGORITHM_RSA_RAW) { + size_t rsize = r; + /* RSA RAW crgram_len == modlen */ + /* removed padding is always > 1 byte */ + /* add back missing leading zero if card dropped it */ + if (rsize == crgram_len - 1 && rsize < outlen) { + memmove(out+1, out, rsize); + out[0] =0x00; + r++; + } + } + + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); + } + if (sc_get_max_send_size(card) < crgram_len + 1) { /* CardOS doesn't support chaining for PSO:DEC, so we just _hope_ * that both, the reader and the card are able to send enough data. @@ -1003,7 +1240,7 @@ cardos_decipher(struct sc_card *card, card->max_send_size = card_max_send_size; card->reader->max_send_size = reader_max_send_size; - return r; + SC_FUNC_RETURN(card->ctx, SC_LOG_DEBUG_VERBOSE, r); } static int @@ -1188,7 +1425,7 @@ static int cardos_get_serialnr(sc_card_t *card, sc_serial_number_t *serial) LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); if (apdu.sw1 != 0x90 || apdu.sw2 != 0x00) return SC_ERROR_INTERNAL; - if ((apdu.resplen == 8) && (card->type == SC_CARD_TYPE_CARDOS_V5_0)) { + if ((apdu.resplen == 8) && (card->type == SC_CARD_TYPE_CARDOS_V5_0 || card->type == SC_CARD_TYPE_CARDOS_V5_3)) { /* cache serial number */ memcpy(card->serialnr.value, rbuf, 8); card->serialnr.len = 8; @@ -1223,6 +1460,9 @@ cardos_card_ctl(sc_card_t *card, unsigned long cmd, void *ptr) case SC_CARDCTL_CARDOS_GENERATE_KEY: return cardos_generate_key(card, (struct sc_cardctl_cardos_genkey_info *) ptr); + case SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS: + return cardos_pass_algo_flags(card, + (struct sc_cardctl_cardos_pass_algo_flags *) ptr); case SC_CARDCTL_LIFECYCLE_GET: return cardos_lifecycle_get(card, (int *) ptr); case SC_CARDCTL_LIFECYCLE_SET: @@ -1279,7 +1519,8 @@ cardos_logout(sc_card_t *card) || card->type == SC_CARD_TYPE_CARDOS_M4_2C || card->type == SC_CARD_TYPE_CARDOS_M4_3 || card->type == SC_CARD_TYPE_CARDOS_M4_4 - || card->type == SC_CARD_TYPE_CARDOS_V5_0) { + || card->type == SC_CARD_TYPE_CARDOS_V5_0 + || card->type == SC_CARD_TYPE_CARDOS_V5_3) { sc_apdu_t apdu; int r; sc_path_t path; @@ -1309,6 +1550,7 @@ static struct sc_card_driver * sc_get_driver(void) cardos_ops = *iso_ops; cardos_ops.match_card = cardos_match_card; cardos_ops.init = cardos_init; + cardos_ops.finish = cardos_finish; cardos_ops.select_file = cardos_select_file; cardos_ops.create_file = cardos_create_file; cardos_ops.set_security_env = cardos_set_security_env; diff --git a/src/libopensc/cardctl.h b/src/libopensc/cardctl.h index 0c68c4c6..10ddfa05 100644 --- a/src/libopensc/cardctl.h +++ b/src/libopensc/cardctl.h @@ -83,6 +83,7 @@ enum { SC_CARDCTL_CARDOS_PUT_DATA_OCI, SC_CARDCTL_CARDOS_PUT_DATA_SECI, SC_CARDCTL_CARDOS_GENERATE_KEY, + SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, /* * Starcos SPK 2.3 specific calls @@ -350,6 +351,14 @@ typedef struct sc_cardctl_pkcs11_init_pin { size_t pin_len; } sc_cardctl_pkcs11_init_pin_t; +/* + * Generic cardctl - card driver can examine token info + */ +struct sc_cardctl_parsed_token_info { + unsigned int flags; + struct sc_pkcs15_tokeninfo * tokeninfo; +}; + /* * GPK lock file. * Parent DF of file must be selected. @@ -419,6 +428,15 @@ struct sc_cardctl_cardos_genkey_info { unsigned short fid; }; +struct sc_cardctl_cardos_pass_algo_flags { + unsigned int pass; + unsigned long card_flags; /* from card->flags i.e. user set */ + unsigned long used_flags; /* as set by default */ + unsigned long new_flags; /* set in pkcs15-cardos.c */ + unsigned long ec_flags; /* for EC keys */ + unsigned long ext_flags; /* for EC keys */ +}; + /* * Incrypto34 PIN info */ diff --git a/src/libopensc/cards.h b/src/libopensc/cards.h index cb0501c3..8d58fb93 100644 --- a/src/libopensc/cards.h +++ b/src/libopensc/cards.h @@ -47,6 +47,7 @@ enum { SC_CARD_TYPE_CARDOS_CIE_V1, /* Italian CIE (eID) v1 */ SC_CARD_TYPE_CARDOS_M4_4, SC_CARD_TYPE_CARDOS_V5_0, + SC_CARD_TYPE_CARDOS_V5_3, /* flex/cyberflex drivers */ SC_CARD_TYPE_FLEX_BASE = 2000, diff --git a/src/libopensc/pkcs15-cardos.c b/src/libopensc/pkcs15-cardos.c new file mode 100644 index 00000000..752631ce --- /dev/null +++ b/src/libopensc/pkcs15-cardos.c @@ -0,0 +1,177 @@ +/* + * PKCS15 emulation layer for CardOS cards + * Adapted from PKCS15 emulation layer for IAS/ECC card. + * + * Copyright (C) 2020, Douglas E. Engert + * Copyright (C) 2016, Viktor Tarasov + * 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 + */ + +#if HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#include "internal.h" +#include "pkcs15.h" + + +/* + * Called after sc_pkcs15_bind_internal + * Create new flags based on supported_algos. + */ +static int cardos_fix_token_info(sc_pkcs15_card_t *p15card) +{ + sc_card_t *card; + struct sc_supported_algo_info (*saa)[SC_MAX_SUPPORTED_ALGORITHMS]; + struct sc_supported_algo_info *sa; + struct sc_cardctl_cardos_pass_algo_flags *passed = NULL; + int r = 0; + int i; + + card = p15card->card; + + SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE); + + passed = calloc(1, sizeof(struct sc_cardctl_cardos_pass_algo_flags)); + if (!passed) + LOG_FUNC_RETURN(card->ctx, SC_ERROR_NOT_ENOUGH_MEMORY); + + passed->pass = 1; /* get used_flags and card_flags from card */ + r = sc_card_ctl(p15card->card, SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, passed); + if (r < 0) { + free(passed); + LOG_FUNC_RETURN(card->ctx, r); + } + + saa = &(p15card->tokeninfo->supported_algos); + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "Original Flags: 0x%8.8lx card->flags:0x%8.8lx", passed->used_flags, passed->card_flags); + + if (passed->card_flags) { /* user forced the flags, use them */ + passed->new_flags = passed->card_flags; /* from card_atr flags */ + } else { + + for (i = 0, sa = saa[0]; i < SC_MAX_SUPPORTED_ALGORITHMS; i++, sa++) { + + if (sa->reference == 0 && sa->reference == 0 && sa->mechanism == 0 + && sa->operations == 0 && sa->algo_ref == 0) + break; + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "supported_algos[%d] mechamism:0x%8.8x", i, sa->mechanism); + switch(sa->mechanism) { + case 0x01 : + /* + * Card appears to use lower 4 bits of reference as key, and upper + * 4 bits as mech for card. + * Also has a bug if mechanism = 1 (CKM_RSA_PKCS1) and reference 0x10 + * bit is set mechanism should be 3 (CKM_RSA_X_509) + * correct the mechanism in tokenInfo + */ + if (sa->reference & 0x10) { + sc_log(card->ctx, "Changeing mechanism to CKM_RSA_X_509 based on reference"); + passed->new_flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_PAD_NONE; + sa->mechanism = 0x03; + } else + passed->new_flags |= SC_ALGORITHM_RSA_PAD_PKCS1; + break; + case 0x03 : + passed->new_flags |= SC_ALGORITHM_RSA_RAW + | SC_ALGORITHM_RSA_PAD_NONE; + break; + case 0x06 : + passed->new_flags |= SC_ALGORITHM_RSA_HASH_SHA1; + break; + case 0x1041: + passed->ec_flags |= SC_ALGORITHM_ECDSA_RAW; + /* no old_ec_flags */ + /* TODO turn on sizes from ec curves OIDS */ + break; + default: + sc_debug(card->ctx, SC_LOG_DEBUG_VERBOSE, "UNKNOWN MECH: 0x%8.8x", sa->mechanism); + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "New_flags 0x%8.8lx New_ec_flags: 0x%8.8lx", + passed->new_flags, passed->ec_flags); + } + + if (passed->new_flags == 0) { + if (p15card->tokeninfo && p15card->tokeninfo->flags & SC_PKCS15_TOKEN_EID_COMPLIANT) { + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL, "EID_COMPLIANT flag found"); + passed->new_flags = (passed->used_flags & ~SC_ALGORITHM_SPECIFIC_FLAGS) | SC_ALGORITHM_RSA_PAD_PKCS1; + } else + passed->new_flags = passed->used_flags; /* from default cardos_init */ + } + } + + sc_debug(card->ctx, SC_LOG_DEBUG_NORMAL,"Final New_flags 0x%8.8lx New_ec_flags: 0x%8.8lx", passed->new_flags, passed->ec_flags); + + passed->pass = 2; /* tell card driver to use the new flags */ + r = sc_card_ctl(p15card->card, SC_CARDCTL_CARDOS_PASS_ALGO_FLAGS, passed); + + free(passed); + LOG_FUNC_RETURN(card->ctx, r); +} + +static int +cardos_pkcs15emu_detect_card(sc_pkcs15_card_t *p15card) +{ + if (p15card->card->type < SC_CARD_TYPE_CARDOS_BASE) + return SC_ERROR_WRONG_CARD; + + if (p15card->card->type >= SC_CARD_TYPE_CARDOS_BASE + 1000) + return SC_ERROR_WRONG_CARD; + + return SC_SUCCESS; +} + + +static int +sc_pkcs15emu_cardos_init(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + sc_card_t *card = p15card->card; + int r; + + LOG_FUNC_CALLED(card->ctx); + + r = sc_pkcs15_bind_internal(p15card, aid); + LOG_TEST_RET(card->ctx, r, "sc_pkcs15_bind_internal failed"); + + /* If card has created algorithms, return */ + sc_log(card->ctx, " card->algorithms:%p card->algorithm_count:%d", card->algorithms, card->algorithm_count); + if (!card->algorithms && card->algorithm_count == 0) { + r = cardos_fix_token_info(p15card); + } + + LOG_FUNC_RETURN(card->ctx, r); +} + + +int +sc_pkcs15emu_cardos_init_ex(struct sc_pkcs15_card *p15card, struct sc_aid *aid) +{ + if (cardos_pkcs15emu_detect_card(p15card)) + return SC_ERROR_WRONG_CARD; + + return sc_pkcs15emu_cardos_init(p15card, aid); +} diff --git a/src/libopensc/pkcs15-syn.c b/src/libopensc/pkcs15-syn.c index aba6f52c..ecd06b13 100644 --- a/src/libopensc/pkcs15-syn.c +++ b/src/libopensc/pkcs15-syn.c @@ -60,6 +60,7 @@ struct sc_pkcs15_emulator_handler builtin_emulators[] = { { "coolkey", sc_pkcs15emu_coolkey_init_ex }, { "din66291", sc_pkcs15emu_din_66291_init_ex }, { "esteid2018", sc_pkcs15emu_esteid2018_init_ex }, + { "cardos", sc_pkcs15emu_cardos_init_ex }, { NULL, NULL } }; @@ -95,6 +96,8 @@ int sc_pkcs15_is_emulation_only(sc_card_t *card) case SC_CARD_TYPE_PIV_II_NEO: case SC_CARD_TYPE_PIV_II_YUBIKEY4: case SC_CARD_TYPE_ESTEID_2018: + case SC_CARD_TYPE_CARDOS_V5_0: + case SC_CARD_TYPE_CARDOS_V5_3: return 1; default: diff --git a/src/libopensc/pkcs15-syn.h b/src/libopensc/pkcs15-syn.h index ccaf693c..a15e0d95 100644 --- a/src/libopensc/pkcs15-syn.h +++ b/src/libopensc/pkcs15-syn.h @@ -54,6 +54,7 @@ int sc_pkcs15emu_jpki_init_ex(sc_pkcs15_card_t *, struct sc_aid *); int sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); int sc_pkcs15emu_din_66291_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); int sc_pkcs15emu_idprime_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); +int sc_pkcs15emu_cardos_init_ex(sc_pkcs15_card_t *p15card, struct sc_aid *); struct sc_pkcs15_emulator_handler { const char *name;