authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd,
		int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct sc_apdu apdu;
	struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1;
	int rv;

	LOG_FUNC_CALLED(ctx);
	sc_log(ctx, "CHV PIN reference %i, pin1(%p,len:%i)", pin_cmd->pin_reference, pin1->data, pin1->len);

	if (pin1->data && !pin1->len)   {
		sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference);
	}
	else if (pin1->data && pin1->len)   {
		unsigned char pin_buff[SC_MAX_APDU_BUFFER_SIZE];
		size_t pin_len;

		memcpy(pin_buff, pin1->data, pin1->len);
		pin_len = pin1->len;

		if (pin1->pad_length && pin_cmd->flags & SC_PIN_CMD_NEED_PADDING)   {
			memset(pin_buff + pin1->len, pin1->pad_char, pin1->pad_length - pin1->len);
			pin_len = pin1->pad_length;
		}

		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference);
		apdu.data = pin_buff;
		apdu.datalen = pin_len;
		apdu.lc = pin_len;
	}
	else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin1->data && !pin1->len)   {
		rv = authentic_chv_verify_pinpad(card, pin_cmd, tries_left);
		sc_log(ctx, "authentic_chv_verify() authentic_chv_verify_pinpad returned %i", rv);
		LOG_FUNC_RETURN(ctx, rv);
	}
	else   {
		LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
	}

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "APDU transmit failed");

	if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0)   {
		pin1->tries_left = apdu.sw2 & 0x0F;
		if (tries_left)
			*tries_left = apdu.sw2 & 0x0F;
	}

	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);

	LOG_FUNC_RETURN(ctx, rv);
}
authentic_chv_verify(struct sc_card *card, struct sc_pin_cmd_data *pin_cmd,
		int *tries_left)
{
	struct sc_context *ctx = card->ctx;
	struct sc_apdu apdu;
	struct sc_pin_cmd_pin *pin1 = &pin_cmd->pin1;
	int rv;

	LOG_FUNC_CALLED(ctx);
	sc_log(ctx, "CHV PIN reference %i, pin1(%p,len:%i)", pin_cmd->pin_reference, pin1->data, pin1->len);

	if (pin1->data && !pin1->len)   {
		sc_format_apdu(card, &apdu, SC_APDU_CASE_1, 0x20, 0, pin_cmd->pin_reference);
	}
	else if (pin1->data && pin1->len)   {
		unsigned char pin_buff[SC_MAX_APDU_BUFFER_SIZE];
		size_t pin_len;

		memcpy(pin_buff, pin1->data, pin1->len);
		pin_len = pin1->len;

		if (pin1->pad_length && pin_cmd->flags & SC_PIN_CMD_NEED_PADDING)   {
			memset(pin_buff + pin1->len, pin1->pad_char, pin1->pad_length - pin1->len);
			pin_len = pin1->pad_length;
		}

		sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, pin_cmd->pin_reference);
		apdu.data = pin_buff;
		apdu.datalen = pin_len;
		apdu.lc = pin_len;
	}
	else if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD) && !pin1->data && !pin1->len)   {
		rv = authentic_chv_verify_pinpad(card, pin_cmd, tries_left);
		sc_log(ctx, "authentic_chv_verify() authentic_chv_verify_pinpad returned %i", rv);
		LOG_FUNC_RETURN(ctx, rv);
	}
	else   {
		LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
	}

	rv = sc_transmit_apdu(card, &apdu);
	LOG_TEST_RET(ctx, rv, "APDU transmit failed");

	if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0)   {
		pin1->tries_left = apdu.sw2 & 0x0F;
		if (tries_left)
			*tries_left = apdu.sw2 & 0x0F;
	}

	rv = sc_check_sw(card, apdu.sw1, apdu.sw2);

	LOG_FUNC_RETURN(ctx, rv);
}