Merge pull request #530 from NWilson/yubikey-neo-pin
Yubikey NEO pin functions support
This commit is contained in:
commit
a906c6d7b8
|
@ -170,7 +170,7 @@ static int myeid_init(struct sc_card *card)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* State that we have an RNG */
|
/* 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_recv_size = 255;
|
||||||
card->max_send_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));
|
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,
|
static int myeid_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
|
||||||
int *tries_left)
|
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);
|
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",
|
sc_log(card->ctx, "ref (%d), pin1 len(%d), pin2 len (%d)\n",
|
||||||
data->pin_reference, data->pin1.len, data->pin2.len);
|
data->pin_reference, data->pin1.len, data->pin2.len);
|
||||||
|
|
||||||
|
|
|
@ -2895,7 +2895,7 @@ static int piv_init(sc_card_t *card)
|
||||||
_sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL);
|
_sc_card_add_ec_alg(card, 256, flags, ext_flags, NULL);
|
||||||
_sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL);
|
_sc_card_add_ec_alg(card, 384, flags, ext_flags, NULL);
|
||||||
|
|
||||||
card->caps |= SC_CARD_CAP_RNG;
|
card->caps |= SC_CARD_CAP_RNG | SC_CARD_CAP_ISO7816_PIN_INFO;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* 800-73-3 cards may have a history object and/or a discovery object
|
* 800-73-3 cards may have a history object and/or a discovery object
|
||||||
|
@ -2912,6 +2912,27 @@ static int piv_init(sc_card_t *card)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static int piv_check_sw(struct sc_card *card, unsigned int sw1, unsigned int sw2)
|
||||||
|
{
|
||||||
|
struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
|
||||||
|
|
||||||
|
const u8 *yubikey_neo_atr =
|
||||||
|
(const u8*)"\x3B\xFC\x13\x00\x00\x81\x31\xFE\x15\x59\x75\x62\x69\x6B\x65\x79\x4E\x45\x4F\x72\x33\xE1";
|
||||||
|
if (card->atr.len != 22 || memcmp(card->atr.value, yubikey_neo_atr, 22) != 0)
|
||||||
|
return iso_drv->ops->check_sw(card, sw1, sw2);
|
||||||
|
|
||||||
|
/* Handle here the Yubikey NEO, which returns 0x0X rather than 0xCX to
|
||||||
|
* indicate the number of remaining PIN retries. Perhaps they misread the
|
||||||
|
* spec and thought 0xCX meant "clear" or "don't care", not a literal 0xC! */
|
||||||
|
if (sw1 == 0x63U && (sw2 & ~0x0fU) == 0x00U && sw2 != 0) {
|
||||||
|
sc_log(card->ctx, "Verification failed (remaining tries: %d)", (sw2 & 0x0f));
|
||||||
|
return SC_ERROR_PIN_CODE_INCORRECT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iso_drv->ops->check_sw(card, sw1, sw2);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
|
static int piv_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data,
|
||||||
int *tries_left)
|
int *tries_left)
|
||||||
{
|
{
|
||||||
|
@ -2952,6 +2973,7 @@ static struct sc_card_driver * sc_get_driver(void)
|
||||||
piv_ops.restore_security_env = piv_restore_security_env;
|
piv_ops.restore_security_env = piv_restore_security_env;
|
||||||
piv_ops.compute_signature = piv_compute_signature;
|
piv_ops.compute_signature = piv_compute_signature;
|
||||||
piv_ops.decipher = piv_decipher;
|
piv_ops.decipher = piv_decipher;
|
||||||
|
piv_ops.check_sw = piv_check_sw;
|
||||||
piv_ops.card_ctl = piv_card_ctl;
|
piv_ops.card_ctl = piv_card_ctl;
|
||||||
piv_ops.pin_cmd = piv_pin_cmd;
|
piv_ops.pin_cmd = piv_pin_cmd;
|
||||||
|
|
||||||
|
|
|
@ -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
|
* Encode 16 hexadecimals of SO-PIN into binary form
|
||||||
* Caller must check length of sopin and provide an 8 byte buffer
|
* 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;
|
sc_hsm_private_data_t *priv = (sc_hsm_private_data_t *) card->drv_data;
|
||||||
int r;
|
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->cmd == SC_PIN_CMD_VERIFY) && (data->pin_reference == 0x88)) {
|
||||||
if (data->pin1.len != 16)
|
if (data->pin1.len != 16)
|
||||||
return SC_ERROR_INVALID_PIN_LENGTH;
|
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, 256, flags, ext_flags, NULL);
|
||||||
_sc_card_add_ec_alg(card, 320, 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_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
|
card->max_recv_size = 0; // Card supports sending with extended length APDU and without limit
|
||||||
|
|
|
@ -969,6 +969,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)
|
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 r, len = 0, pad = 0, use_pin_pad = 0, ins, p1 = 0;
|
||||||
|
int cse = SC_APDU_CASE_3_SHORT;
|
||||||
|
|
||||||
switch (data->pin_type) {
|
switch (data->pin_type) {
|
||||||
case SC_AC_CHV:
|
case SC_AC_CHV:
|
||||||
|
@ -1034,11 +1035,16 @@ iso7816_build_pin_apdu(struct sc_card *card, struct sc_apdu *apdu,
|
||||||
p1 |= 0x01;
|
p1 |= 0x01;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case SC_PIN_CMD_GET_INFO:
|
||||||
|
ins = 0x20;
|
||||||
|
/* No data to send or to receive */
|
||||||
|
cse = SC_APDU_CASE_1;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
return SC_ERROR_NOT_SUPPORTED;
|
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->lc = len;
|
||||||
apdu->datalen = len;
|
apdu->datalen = len;
|
||||||
apdu->data = buf;
|
apdu->data = buf;
|
||||||
|
@ -1058,6 +1064,16 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l
|
||||||
if (tries_left)
|
if (tries_left)
|
||||||
*tries_left = -1;
|
*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
|
/* See if we've been called from another card driver, which is
|
||||||
* passing an APDU to us (this allows to write card drivers
|
* passing an APDU to us (this allows to write card drivers
|
||||||
* whose PIN functions behave "mostly like ISO" except in some
|
* whose PIN functions behave "mostly like ISO" except in some
|
||||||
|
@ -1071,7 +1087,7 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l
|
||||||
}
|
}
|
||||||
apdu = data->apdu;
|
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 */
|
/* Transmit the APDU to the card */
|
||||||
r = sc_transmit_apdu(card, apdu);
|
r = sc_transmit_apdu(card, apdu);
|
||||||
|
|
||||||
|
@ -1101,12 +1117,23 @@ iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_l
|
||||||
data->apdu = NULL;
|
data->apdu = NULL;
|
||||||
|
|
||||||
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
LOG_TEST_RET(card->ctx, r, "APDU transmit failed");
|
||||||
if (apdu->sw1 == 0x63) {
|
r = sc_check_sw(card, apdu->sw1, apdu->sw2);
|
||||||
if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL)
|
|
||||||
*tries_left = apdu->sw2 & 0x0F;
|
if (data->cmd == SC_PIN_CMD_GET_INFO) {
|
||||||
return SC_ERROR_PIN_CODE_INCORRECT;
|
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;
|
||||||
}
|
}
|
||||||
return sc_check_sw(card, apdu->sw1, apdu->sw2);
|
|
||||||
|
if (tries_left != NULL) {
|
||||||
|
*tries_left = data->pin1.tries_left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -439,6 +439,9 @@ struct sc_reader_operations {
|
||||||
/* Card has on-board random number source. */
|
/* Card has on-board random number source. */
|
||||||
#define SC_CARD_CAP_RNG 0x00000004
|
#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(),
|
/* Use the card's ACs in sc_pkcs15init_authenticate(),
|
||||||
* instead of relying on the ACL info in the profile files. */
|
* instead of relying on the ACL info in the profile files. */
|
||||||
#define SC_CARD_CAP_USE_FCI_AC 0x00000010
|
#define SC_CARD_CAP_USE_FCI_AC 0x00000010
|
||||||
|
|
Loading…
Reference in New Issue