diff --git a/src/libopensc/card-myeid.c b/src/libopensc/card-myeid.c index 9d6417f1..ead6ba9a 100644 --- a/src/libopensc/card-myeid.c +++ b/src/libopensc/card-myeid.c @@ -170,7 +170,7 @@ static int myeid_init(struct sc_card *card) #endif /* State that we have an RNG */ - card->caps |= SC_CARD_CAP_RNG; + card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO; card->max_recv_size = 255; card->max_send_size = 255; @@ -524,42 +524,6 @@ static int myeid_delete_file(struct sc_card *card, const struct sc_path *path) LOG_FUNC_RETURN(card->ctx, sc_check_sw(card, apdu.sw1, apdu.sw2)); } -static int myeid_pin_info(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) -{ - sc_apdu_t apdu; - int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, data->pin_reference); - - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - - if (r == SC_ERROR_PIN_CODE_INCORRECT) { - data->pin1.tries_left = apdu.sw2 & 0xF; - r = SC_SUCCESS; - } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { - data->pin1.tries_left = 0; - r = SC_SUCCESS; - } - LOG_TEST_RET(card->ctx, r, "Check SW error"); - - if (r == SC_SUCCESS) - { - data->pin1.pad_length = data->pin2.pad_length = 8; - data->pin1.pad_char = data->pin2.pad_char = 0xFF; - } - - - if (tries_left != NULL) { - *tries_left = data->pin1.tries_left; - } - - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); -} - static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left) { @@ -567,11 +531,6 @@ static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, LOG_FUNC_CALLED(card->ctx); - if (data->cmd == SC_PIN_CMD_GET_INFO) - { - return myeid_pin_info(card, data, tries_left); - } - sc_log(card->ctx, "ref (%d), pin1 len(%d), pin2 len (%d)\n", data->pin_reference, data->pin1.len, data->pin2.len); diff --git a/src/libopensc/card-sc-hsm.c b/src/libopensc/card-sc-hsm.c index 87d563b2..b02c84e0 100644 --- a/src/libopensc/card-sc-hsm.c +++ b/src/libopensc/card-sc-hsm.c @@ -137,37 +137,6 @@ static int sc_hsm_match_card(struct sc_card *card) -static int sc_hsm_pin_info(sc_card_t *card, struct sc_pin_cmd_data *data, - int *tries_left) -{ - sc_apdu_t apdu; - int r; - - sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0x00, data->pin_reference); - - r = sc_transmit_apdu(card, &apdu); - LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - - r = sc_check_sw(card, apdu.sw1, apdu.sw2); - - if (r == SC_ERROR_PIN_CODE_INCORRECT) { - data->pin1.tries_left = apdu.sw2 & 0xF; - r = SC_SUCCESS; - } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { - data->pin1.tries_left = 0; - r = SC_SUCCESS; - } - LOG_TEST_RET(card->ctx, r, "Check SW error"); - - if (tries_left != NULL) { - *tries_left = data->pin1.tries_left; - } - - LOG_FUNC_RETURN(card->ctx, SC_SUCCESS); -} - - - /* * Encode 16 hexadecimals of SO-PIN into binary form * Caller must check length of sopin and provide an 8 byte buffer @@ -206,9 +175,6 @@ static int sc_hsm_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data; int r; - if (data->cmd == SC_PIN_CMD_GET_INFO) { - return sc_hsm_pin_info(card, data, tries_left); - } if ((data->cmd == SC_PIN_CMD_VERIFY) && (data->pin_reference == 0x88)) { if (data->pin1.len != 16) return SC_ERROR_INVALID_PIN_LENGTH; @@ -1058,7 +1024,7 @@ static int sc_hsm_init(struct sc_card *card) _sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL); _sc_card_add_ec_alg(card, 320, flags, ext_flags, NULL); - card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT; + card->caps |= SC_CARD_CAP_RNG|SC_CARD_CAP_APDU_EXT|SC_CARD_CAP_ISO7816_PIN_INFO; card->max_send_size = 1431; // 1439 buffer size - 8 byte TLV because of odd ins in UPDATE BINARY card->max_recv_size = 0; // Card supports sending with extended length APDU and without limit diff --git a/src/libopensc/iso7816.c b/src/libopensc/iso7816.c index baa15814..d0bf8b4f 100644 --- a/src/libopensc/iso7816.c +++ b/src/libopensc/iso7816.c @@ -987,6 +987,7 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, struct sc_pin_cmd_data *data, u8 *buf, size_t buf_len) { int r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0; + int cse = SC_APDU_CASE_3_SHORT; switch (data->pin_type) { case SC_AC_CHV: @@ -1052,11 +1053,16 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu, p1 |= 0x01; } break; + case SC_PIN_CMD_GET_INFO: + ins = 0x20; + /* No data to send or to receive */ + cse = SC_APDU_CASE_1; + break; default: return SC_ERROR_NOT_SUPPORTED; } - sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, p1, data->pin_reference); + sc_format_apdu(card, apdu, cse, ins, p1, data->pin_reference); apdu->lc = len; apdu->datalen = len; apdu->data = buf; @@ -1076,6 +1082,16 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l if (tries_left) *tries_left = -1; + /* Many cards do support PIN status queries, but some cards don't and + * mistakenly count the command as a failed PIN attempt, so for now we + * whitelist cards with this flag. In future this may be reduced to a + * blacklist, subject to testing more cards. */ + if (data->cmd == SC_PIN_CMD_GET_INFO && + !(card->caps & SC_CARD_CAP_ISO7816_PIN_INFO)) { + sc_log(card->ctx, "Card does not support PIN status queries"); + return SC_ERROR_NOT_SUPPORTED; + } + /* See if we've been called from another card driver, which is * passing an APDU to us (this allows to write card drivers * whose PIN functions behave "mostly like ISO" except in some @@ -1089,7 +1105,7 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l } apdu = data->apdu; - if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) { + if (!(data->flags & SC_PIN_CMD_USE_PINPAD) || data->cmd == SC_PIN_CMD_GET_INFO) { /* Transmit the APDU to the card */ r = sc_transmit_apdu(card, apdu); @@ -1119,12 +1135,23 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l data->apdu = NULL; LOG_TEST_RET(card->ctx, r, "APDU transmit failed"); - if (apdu->sw1 == 0x63) { - if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL) - *tries_left = apdu->sw2 & 0x0F; - return SC_ERROR_PIN_CODE_INCORRECT; + r = sc_check_sw(card, apdu->sw1, apdu->sw2); + + if (data->cmd == SC_PIN_CMD_GET_INFO) { + if (r == SC_ERROR_PIN_CODE_INCORRECT) { + data->pin1.tries_left = apdu->sw2 & 0xF; + r = SC_SUCCESS; + } else if (r == SC_ERROR_AUTH_METHOD_BLOCKED) { + data->pin1.tries_left = 0; + r = SC_SUCCESS; + } + + if (tries_left != NULL) { + *tries_left = data->pin1.tries_left; + } } - return sc_check_sw(card, apdu->sw1, apdu->sw2); + + return r; } diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index e9a4d19a..69fdd34a 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -439,6 +439,9 @@ struct sc_reader_operations { /* Card has on-board random number source. */ #define SC_CARD_CAP_RNG 0x00000004 +/* Card supports ISO7816 PIN status queries using an empty VERIFY */ +#define SC_CARD_CAP_ISO7816_PIN_INFO 0x00000008 + /* Use the card's ACs in sc_pkcs15init_authenticate(), * instead of relying on the ACL info in the profile files. */ #define SC_CARD_CAP_USE_FCI_AC 0x00000010