From e13b92b003e318cde5518aadcf99aa9438e0fd15 Mon Sep 17 00:00:00 2001 From: frankmorgner Date: Mon, 8 Mar 2010 22:16:47 +0000 Subject: [PATCH] - bugfix: general authenticate response data is now encapsulated in a dynamic data object - added more debug messages - bugfix: get_ef_card_access didn't fetch the whole file - bugfix: set mse set at apdu type to case 3 - bugfix: only send the plain value (without tag/length) of the PACE_MSE_SET_AT object - bugfix: fixed general authenticate cla byte and set apdu type to case 4 - sanity checks in sc_transmit_apdu forbid the required apdu format of general authenticate so it had to be reimplemented in my_transmit_apdu. apdu.c/apdu.h are mainly copied from opensc. git-svn-id: https://vsmartcard.svn.sourceforge.net/svnroot/vsmartcard@44 96b47cad-a561-4643-ad3b-153ac7d7599c --- ccid/apdu.c | 306 ++++++++++++++++++++++++++++++++++++++++++++++++++++ ccid/apdu.h | 20 ++++ ccid/pace.c | 116 +++++++++++++++----- 3 files changed, 414 insertions(+), 28 deletions(-) create mode 100644 ccid/apdu.c create mode 100644 ccid/apdu.h diff --git a/ccid/apdu.c b/ccid/apdu.c new file mode 100644 index 0000000..87988ae --- /dev/null +++ b/ccid/apdu.c @@ -0,0 +1,306 @@ +/* + * apdu.c: basic APDU handling functions + * + * Copyright (C) 2005 Nils Larsch + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include "apdu.h" +#include +#include + +/*********************************************************************/ +/* higher level APDU transfer handling functions */ +/*********************************************************************/ +/* +------------------+ + * | sc_transmit_apdu | + * +------------------+ + * | | + * | | detect APDU cse +--------------------+ + * | +---------------------------------> | sc_detect_apdu_cse | + * | +--------------------+ + * | + * | + * | + * | send single APDU +--------------------+ + * +---------------------------------------> | do_single_transmit | + * ^ +--------------------+ + * | | + * | re-transmit if wrong length | + * | or GET RESPONSE | + * +-------------------------------+ + * | + * v + * card->reader->ops->tranmit + */ + + +/** Tries to determine the APDU type (short or extended) of the supplied + * APDU if one of the SC_APDU_CASE_? types is used. + * @param apdu APDU object + */ +static void sc_detect_apdu_cse(const sc_card_t *card, sc_apdu_t *apdu) +{ + if (apdu->cse == SC_APDU_CASE_2 || apdu->cse == SC_APDU_CASE_3 || + apdu->cse == SC_APDU_CASE_4) { + int btype = apdu->cse & SC_APDU_SHORT_MASK; + /* if either Lc or Le is bigger than the maximun for + * short APDUs and the card supports extended APDUs + * use extended APDUs (unless Lc is greater than + * 255 and command chaining is activated) */ + if ((apdu->le > 256 || (apdu->lc > 255 && (apdu->flags & SC_APDU_FLAGS_CHAINING) == 0)) && + (card->caps & SC_CARD_CAP_APDU_EXT) != 0) + btype |= SC_APDU_EXT; + apdu->cse = btype; + } +} + + +/** Sends a single APDU to the card reader and calls + * GET RESPONSE to get the return data if necessary. + * @param card sc_card_t object for the smartcard + * @param apdu APDU to be send + * @return SC_SUCCESS on success and an error value otherwise + */ +static int do_single_transmit(sc_card_t *card, sc_apdu_t *apdu) +{ + int r; + size_t olen = apdu->resplen; + sc_context_t *ctx = card->ctx; + + /* XXX: insert secure messaging here (?), i.e. something like + if (card->sm_ctx->use_sm != 0) { + r = card->ops->sm_transform(...); + if (r != SC_SUCCESS) + ... + r = sc_check_apdu(...); + if (r != SC_SUCCESS) + ... + } + */ + + /* send APDU to the reader driver */ + if (card->reader->ops->transmit == NULL) + return SC_ERROR_NOT_SUPPORTED; + r = card->reader->ops->transmit(card->reader, card->slot, apdu); + if (r != 0) { + sc_error(ctx, "unable to transmit APDU"); + return r; + } + /* ok, the APDU was successfully transmitted. Now we have two + * special cases: + * 1. the card returned 0x6Cxx: in this case we re-trasmit the APDU + * wit hLe set to SW2 (this is course only possible if the + * response buffer size is larger than the new Le = SW2) + */ + if (apdu->sw1 == 0x6C && (apdu->flags & SC_APDU_FLAGS_NO_RETRY_WL) == 0) { + size_t nlen = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256; + if (olen >= nlen) { + /* don't try again if it doesn't work this time */ + apdu->flags |= SC_APDU_FLAGS_NO_GET_RESP; + /* set the new expected length */ + apdu->resplen = olen; + apdu->le = nlen; + /* as some reader/smartcards can't handle an immediate + * re-transmit so we optionally need to sleep for + * a while */ + if (card->wait_resend_apdu != 0) + msleep(card->wait_resend_apdu); + /* re-transmit the APDU with new Le length */ + r = card->reader->ops->transmit(card->reader, card->slot, apdu); + if (r != SC_SUCCESS) { + sc_error(ctx, "unable to transmit APDU"); + return r; + } + } else { + /* we cannot re-transmit the APDU with the demanded + * Le value as the buffer is too small => error */ + sc_debug(ctx, "wrong length: required length exceeds resplen"); + return SC_ERROR_WRONG_LENGTH; + } + } + + /* 2. the card returned 0x61xx: more data can be read from the card + * using the GET RESPONSE command (mostly used in the T0 protocol). + * Unless the SC_APDU_FLAGS_NO_GET_RESP is set we try to read as + * much data as possible using GET RESPONSE. + */ + if (apdu->sw1 == 0x61 && (apdu->flags & SC_APDU_FLAGS_NO_GET_RESP) == 0) { + if (apdu->le == 0) { + /* no data is requested => change return value to + * 0x9000 and ignore the remaining data */ + /* FIXME: why not return 0x61xx ? It's not an + * error */ + apdu->sw1 = 0x90; + apdu->sw2 = 0x00; + + } else { + /* call GET RESPONSE until we have read all data + * requested or until the card retuns 0x9000, + * whatever happens first. + */ + size_t le, minlen, buflen; + u8 *buf; + + if (card->ops->get_response == NULL) { + /* this should _never_ happen */ + sc_error(ctx, "no GET RESPONSE command\n"); + return SC_ERROR_NOT_SUPPORTED; + } + + /* if the command already returned some data + * append the new data to the end of the buffer + */ + buf = apdu->resp + apdu->resplen; + + /* read as much data as fits in apdu->resp (i.e. + * max(apdu->resplen, amount of data available)). + */ + buflen = olen - apdu->resplen; + + /* 0x6100 means at least 256 more bytes to read */ + le = apdu->sw2 != 0 ? (size_t)apdu->sw2 : 256; + /* we try to read at least as much as bytes as + * promised in the response bytes */ + minlen = le; + + do { + u8 tbuf[256]; + /* call GET RESPONSE to get more date from + * the card; note: GET RESPONSE returns the + * amount of data left (== SW2) */ + r = card->ops->get_response(card, &le, tbuf); + if (r < 0) + SC_FUNC_RETURN(ctx, 2, r); + + if (buflen < le) + return SC_ERROR_WRONG_LENGTH; + + memcpy(buf, tbuf, le); + buf += le; + buflen -= le; + + minlen -= le; + if (r != 0) + le = minlen = (size_t)r; + else + /* if the card has returned 0x9000 but + * we still expect data ask for more + * until we have read enough bytes */ + le = minlen; + } while (r != 0 || minlen != 0); + /* we've read all data, let's return 0x9000 */ + apdu->resplen = buf - apdu->resp; + apdu->sw1 = 0x90; + apdu->sw2 = 0x00; + } + } + + return SC_SUCCESS; +} + +int my_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu) +{ + int r = SC_SUCCESS; + + if (card == NULL || apdu == NULL) + return SC_ERROR_INVALID_ARGUMENTS; + + SC_FUNC_CALLED(card->ctx, 4); + + /* determine the APDU type if necessary, i.e. to use + * short or extended APDUs */ + sc_detect_apdu_cse(card, apdu); + /* basic APDU consistency check */ + /*r = sc_check_apdu(card, apdu);*/ + if (r != SC_SUCCESS) + return SC_ERROR_INVALID_ARGUMENTS; + + r = sc_lock(card); /* acquire card lock*/ + if (r != SC_SUCCESS) { + sc_error(card->ctx, "unable to acquire lock"); + return r; + } + + if ((apdu->flags & SC_APDU_FLAGS_CHAINING) != 0) { + /* divide et impera: transmit APDU in chunks with Lc < 255 + * bytes using command chaining */ + size_t len = apdu->datalen; + const u8 *buf = apdu->data; + + while (len != 0) { + size_t plen; + sc_apdu_t tapdu; + int last = 0; + + tapdu = *apdu; + /* clear chaining flag */ + tapdu.flags &= ~SC_APDU_FLAGS_CHAINING; + if (len > 255) { + /* adjust APDU case: in case of CASE 4 APDU + * the intermediate APDU are of CASE 3 */ + if ((tapdu.cse & SC_APDU_SHORT_MASK) == SC_APDU_CASE_4_SHORT) + tapdu.cse--; + /* XXX: the chunk size must be adjusted when + * secure messaging is used */ + plen = 255; + tapdu.cla |= 0x10; + tapdu.le = 0; + /* the intermediate APDU don't expect data */ + tapdu.lc = 0; + tapdu.resplen = 0; + tapdu.resp = NULL; + } else { + plen = len; + last = 1; + } + tapdu.data = buf; + tapdu.datalen = tapdu.lc = plen; + + /*r = sc_check_apdu(card, &tapdu);*/ + if (r != SC_SUCCESS) { + sc_error(card->ctx, "inconsistent APDU while chaining"); + break; + } + + r = do_single_transmit(card, &tapdu); + if (r != SC_SUCCESS) + break; + if (last != 0) { + /* in case of the last APDU set the SW1 + * and SW2 bytes in the original APDU */ + apdu->sw1 = tapdu.sw1; + apdu->sw2 = tapdu.sw2; + apdu->resplen = tapdu.resplen; + } else { + /* otherwise check the status bytes */ + r = sc_check_sw(card, tapdu.sw1, tapdu.sw2); + if (r != SC_SUCCESS) + break; + } + len -= plen; + buf += plen; + } + } else + /* transmit single APDU */ + r = do_single_transmit(card, apdu); + /* all done => release lock */ + if (sc_unlock(card) != SC_SUCCESS) + sc_error(card->ctx, "sc_unlock failed"); + + return r; +} + diff --git a/ccid/apdu.h b/ccid/apdu.h new file mode 100644 index 0000000..3360574 --- /dev/null +++ b/ccid/apdu.h @@ -0,0 +1,20 @@ +#ifndef _PACE_H +#define _PACE_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +/** Sends a APDU to the card + * @param card sc_card_t object to which the APDU should be send + * @param apdu sc_apdu_t object of the APDU to be send + * @return SC_SUCCESS on succcess and an error code otherwise + */ +int my_transmit_apdu(sc_card_t *card, sc_apdu_t *apdu); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/ccid/pace.c b/ccid/pace.c index 64ad93f..eabd543 100644 --- a/ccid/pace.c +++ b/ccid/pace.c @@ -68,18 +68,28 @@ typedef struct pace_gen_auth_cd_st { ASN1_OCTET_STRING *mapping_data; ASN1_OCTET_STRING *eph_pub_key; ASN1_OCTET_STRING *auth_token; -} PACE_GEN_AUTH_C; -ASN1_SEQUENCE(PACE_GEN_AUTH_C) = { +} PACE_GEN_AUTH_C_BODY; +ASN1_SEQUENCE(PACE_GEN_AUTH_C_BODY) = { /* 0x81 * Mapping Data */ - ASN1_IMP_OPT(PACE_GEN_AUTH_C, mapping_data, ASN1_INTEGER, 1), + ASN1_IMP_OPT(PACE_GEN_AUTH_C_BODY, mapping_data, ASN1_INTEGER, 1), /* 0x83 * Ephemeral Public Key */ - ASN1_IMP_OPT(PACE_GEN_AUTH_C, eph_pub_key, ASN1_INTEGER, 3), + ASN1_IMP_OPT(PACE_GEN_AUTH_C_BODY, eph_pub_key, ASN1_INTEGER, 3), /* 0x85 * Authentication Token */ - ASN1_IMP_OPT(PACE_GEN_AUTH_C, auth_token, ASN1_INTEGER, 5), -} ASN1_SEQUENCE_END(PACE_GEN_AUTH_C) + ASN1_IMP_OPT(PACE_GEN_AUTH_C_BODY, auth_token, ASN1_INTEGER, 5), +} ASN1_SEQUENCE_END(PACE_GEN_AUTH_C_BODY) +IMPLEMENT_ASN1_FUNCTIONS(PACE_GEN_AUTH_C_BODY) + +typedef PACE_GEN_AUTH_C_BODY PACE_GEN_AUTH_C; +/* 0x7C + * Dynamic Authentication Data */ +ASN1_ITEM_TEMPLATE(PACE_GEN_AUTH_C) = + ASN1_EX_TEMPLATE_TYPE( + ASN1_TFLG_IMPTAG|ASN1_TFLG_APPLICATION, + 0x1c, PACE_GEN_AUTH_C, PACE_GEN_AUTH_C_BODY) +ASN1_ITEM_TEMPLATE_END(PACE_GEN_AUTH_C) IMPLEMENT_ASN1_FUNCTIONS(PACE_GEN_AUTH_C) /* Protocol Response Data */ @@ -139,6 +149,7 @@ int pace_test(sc_context_t *ctx, sc_card_t *card) { #else #include +#include #include #include #include @@ -147,9 +158,20 @@ int pace_test(sc_context_t *ctx, sc_card_t *card) { #include #include #include +#include "apdu.h" uint16_t ssc = 0; +void bin_log(sc_context_t *ctx, const char *label, const u8 *data, size_t len) +{ + char buf[1024]; + + sc_hex_dump(ctx, data, len, buf, sizeof buf); + sc_debug(ctx, "%s (%u bytes):\n%s" + "======================================================================", + label, len, buf); +} + int GetReadersPACECapabilities(sc_context_t *ctx, sc_card_t *card, const __u8 *in, __u8 **out, size_t *outlen) { if (!out || !outlen) @@ -190,8 +212,9 @@ int get_ef_card_access(sc_context_t *ctx, sc_card_t *card, SC_TEST_RET(ctx, sc_select_file(card, &path, &file), "Could not select EF.CardAccess."); ssc++; + *length_ef_cardaccess = 0; - for (*length_ef_cardaccess = 0; ; *length_ef_cardaccess += r, ssc++) { + while(1) { p = realloc(*ef_cardaccess, *length_ef_cardaccess + maxresp); if (!p) SC_FUNC_RETURN(ctx, SC_LOG_TYPE_DEBUG, SC_ERROR_OUT_OF_MEMORY); @@ -199,7 +222,16 @@ int get_ef_card_access(sc_context_t *ctx, sc_card_t *card, r = sc_read_binary(card, *length_ef_cardaccess, *ef_cardaccess + *length_ef_cardaccess, maxresp, 0); + ssc++; + + if (r > 0 && r != maxresp) { + *length_ef_cardaccess += r; + break; + } + SC_TEST_RET(ctx, r, "Could not read EF.CardAccess."); + + *length_ef_cardaccess += r; } /* test cards only return an empty FCI template, @@ -222,6 +254,7 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, apdu.ins = 0x22; apdu.p1 = 0xc1; apdu.p2 = 0xa4; + apdu.cse = SC_APDU_CASE_3; apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; data = PACE_MSE_SET_AT_C_new(); @@ -234,12 +267,14 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, data->key_reference2 = ASN1_INTEGER_new(); if (!data->cryptographic_mechanism_reference || !data->key_reference1 - || !data->key_reference2) { + || !data->key_reference2 + ) { r = SC_ERROR_OUT_OF_MEMORY; goto err; } if (!ASN1_INTEGER_set(data->key_reference1, secret_key) - || !ASN1_INTEGER_set(data->key_reference2, reference)) { + || !ASN1_INTEGER_set(data->key_reference2, reference) + ) { r = SC_ERROR_INTERNAL; goto err; } @@ -248,9 +283,13 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, r = SC_ERROR_INTERNAL; goto err; } - apdu.data = (const u8 *) d; - apdu.datalen = r; - apdu.lc = r; + /* The tag/length for the sequence (0x30) is omitted in the command apdu. */ + /* FIXME is there a OpenSSL way to get the value only or even a define for + * the tag? */ + apdu.data = sc_asn1_find_tag(ctx, d, r, 0x30, &apdu.datalen); + apdu.lc = apdu.datalen; + + bin_log(ctx, "MSE:Set AT command data", apdu.data, apdu.datalen); r = sc_transmit_apdu(card, &apdu); ssc++; @@ -258,6 +297,7 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, goto err; if (apdu.resplen) { + sc_error(ctx, "MSE:Set AT response data should be empty"); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } @@ -266,8 +306,7 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, if ((apdu.sw2 & 0xc0) == 0xc0) { sc_error(card->ctx, "Verification failed (remaining tries: %d%s)\n", apdu.sw2 & 0x0f, - (apdu.sw2 & 0x0f) == 1? ", password must be resumed": - (apdu.sw2 & 0x0f) == 0? ", password must be unblocked": + (apdu.sw2 & 0x0f) == 1? ", password must be resumed": (apdu.sw2 & 0x0f) == 0? ", password must be unblocked": ""); r = SC_ERROR_PIN_CODE_INCORRECT; goto err; @@ -287,12 +326,13 @@ int pace_mse_set_at(sc_context_t *ctx, sc_card_t *card, err: if (data) { - if (data->cryptographic_mechanism_reference) - ASN1_OBJECT_free(data->cryptographic_mechanism_reference); - if (data->key_reference1) - ASN1_INTEGER_free(data->key_reference1); - if (data->key_reference2) - ASN1_INTEGER_free(data->key_reference2); + // XXX + //if (data->cryptographic_mechanism_reference) + //ASN1_OBJECT_free(data->cryptographic_mechanism_reference); + //if (data->key_reference1) + //ASN1_INTEGER_free(data->key_reference1); + //if (data->key_reference2) + //ASN1_INTEGER_free(data->key_reference2); PACE_MSE_SET_AT_C_free(data); } if (d) @@ -313,9 +353,11 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, int r, l; memset(&apdu, 0, sizeof apdu); + apdu.cla = 0x10; apdu.ins = 0x86; apdu.p1 = 0; apdu.p2 = 0; + apdu.cse = SC_APDU_CASE_4; apdu.flags = SC_APDU_FLAGS_NO_GET_RESP|SC_APDU_FLAGS_NO_RETRY_WL; c_data = PACE_GEN_AUTH_C_new(); @@ -366,7 +408,10 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, apdu.datalen = r; apdu.lc = r; - r = sc_transmit_apdu(card, &apdu); + bin_log(ctx, "General authenticate command data", apdu.data, apdu.datalen); + + /* sanity checks in sc_transmit_apdu forbid case 4 apdus with le == 0 */ + r = my_transmit_apdu(card, &apdu); ssc++; if (r < 0) goto err; @@ -375,18 +420,24 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, if (r < 0) goto err; + bin_log(ctx, "General authenticate response data", apdu.resp, apdu.resplen); + if (!d2i_PACE_GEN_AUTH_R(&r_data, (const unsigned char **) &apdu.resp, apdu.resplen)) { + sc_error(ctx, "Could not parse general authenticate response data."); r = SC_ERROR_INTERNAL; goto err; } - switch(step) { + switch (step) { case 1: if (!r_data->enc_nonce || r_data->mapping_data || r_data->eph_pub_key || r_data->auth_token) { + sc_error(ctx, "Response data of general authenticate for " + "step %d should (only) contain the " + "encrypted nonce.", step); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } @@ -398,6 +449,9 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, || !r_data->mapping_data || r_data->eph_pub_key || r_data->auth_token) { + sc_error(ctx, "Response data of general authenticate for " + "step %d should (only) contain the " + "mapping data.", step); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } @@ -409,6 +463,9 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, || r_data->mapping_data || !r_data->eph_pub_key || r_data->auth_token) { + sc_error(ctx, "Response data of general authenticate for " + "step %d should (only) contain the " + "ephemeral public key.", step); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } @@ -420,6 +477,9 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, || r_data->mapping_data || r_data->eph_pub_key || !r_data->auth_token) { + sc_error(ctx, "Response data of general authenticate for " + "step %d should (only) contain the " + "authentication token.", step); r = SC_ERROR_UNKNOWN_DATA_RECEIVED; goto err; } @@ -441,23 +501,25 @@ int pace_gen_auth(sc_context_t *ctx, sc_card_t *card, err: if (c_data) { + /* FIXME if (c_data->mapping_data) ASN1_OCTET_STRING_free(c_data->mapping_data); if (c_data->eph_pub_key) ASN1_OCTET_STRING_free(c_data->eph_pub_key); if (c_data->auth_token) - ASN1_OCTET_STRING_free(c_data->auth_token); + ASN1_OCTET_STRING_free(c_data->auth_token);*/ PACE_GEN_AUTH_C_free(c_data); } if (d) free(d); if (r_data) { + /* FIXME if (r_data->mapping_data) ASN1_OCTET_STRING_free(r_data->mapping_data); if (r_data->eph_pub_key) ASN1_OCTET_STRING_free(r_data->eph_pub_key); if (r_data->auth_token) - ASN1_OCTET_STRING_free(r_data->auth_token); + ASN1_OCTET_STRING_free(r_data->auth_token);*/ PACE_GEN_AUTH_R_free(r_data); } if (apdu.resplen) @@ -525,7 +587,7 @@ int EstablishPACEChannel(sc_context_t *ctx, sc_card_t *card, __u8 pin_id; size_t length_chat, length_pin, length_cert_desc, length_ef_cardaccess; const __u8 *chat, *pin, *certificate_description; - __u8 *ef_cardaccess; + __u8 *ef_cardaccess = NULL; PACEInfo *info = NULL; PACEDomainParameterInfo *static_dp = NULL, *eph_dp = NULL; BUF_MEM *enc_nonce, *nonce = NULL, *mdata = NULL, *mdata_opp = NULL, @@ -565,15 +627,13 @@ int EstablishPACEChannel(sc_context_t *ctx, sc_card_t *card, if (r < 0) { goto err; } - //printf("%s:%d\n", __FILE__, __LINE__); + bin_log(ctx, "EF.CardAccess", ef_cardaccess, length_ef_cardaccess); if (!parse_ef_card_access(ef_cardaccess, length_ef_cardaccess, &info, &static_dp)) { r = SC_ERROR_INTERNAL; debug_ossl(ctx); goto err; } - //printf("%s:%d\n", __FILE__, __LINE__); - //return 0; r = pace_mse_set_at(ctx, card, info->protocol, pin_id, 1); if (r < 0) { goto err;