better PIN handling
- avoid overloading of the unblocking command, instead perform resuming the pin automatically. - implemented cache for npa card driver - added support for eSign PIN verification - addes support for get pin info cmd
This commit is contained in:
@@ -21,6 +21,7 @@
|
|||||||
#include "iso-sm-internal.h"
|
#include "iso-sm-internal.h"
|
||||||
#include "libopensc/internal.h"
|
#include "libopensc/internal.h"
|
||||||
#include "libopensc/pace.h"
|
#include "libopensc/pace.h"
|
||||||
|
#include "card-npa.h"
|
||||||
#include <npa/boxing.h>
|
#include <npa/boxing.h>
|
||||||
#include <npa/npa.h>
|
#include <npa/npa.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@@ -29,10 +30,11 @@
|
|||||||
#include "libopensc/card.c"
|
#include "libopensc/card.c"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum {
|
struct npa_drv_data {
|
||||||
SC_CARD_TYPE_NPA = 42000,
|
unsigned char *can;
|
||||||
SC_CARD_TYPE_NPA_TEST,
|
size_t can_length;
|
||||||
SC_CARD_TYPE_NPA_ONLINE,
|
unsigned char *ef_cardaccess;
|
||||||
|
size_t ef_cardaccess_length;
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sc_atr_table npa_atrs[] = {
|
static struct sc_atr_table npa_atrs[] = {
|
||||||
@@ -59,24 +61,209 @@ static int npa_match_card(sc_card_t * card)
|
|||||||
|
|
||||||
static int npa_init(sc_card_t * card)
|
static int npa_init(sc_card_t * card)
|
||||||
{
|
{
|
||||||
card->drv_data = NULL;
|
struct npa_drv_data *drv_data;
|
||||||
card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG;
|
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
drv_data = calloc(1, sizeof *drv_data);
|
||||||
|
card->drv_data = drv_data;
|
||||||
|
if (drv_data) {
|
||||||
|
drv_data->can = NULL;
|
||||||
|
drv_data->can_length = 0;
|
||||||
|
drv_data->ef_cardaccess = NULL;
|
||||||
|
drv_data->ef_cardaccess_length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
card->caps |= SC_CARD_CAP_APDU_EXT | SC_CARD_CAP_RNG;
|
||||||
|
memset(&card->sm_ctx, 0, sizeof card->sm_ctx);
|
||||||
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
|
#ifdef DISABLE_GLOBAL_BOXING_INITIALIZATION
|
||||||
sc_detect_boxing_cmds(card->reader);
|
sc_detect_boxing_cmds(card->reader);
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
return SC_SUCCESS;
|
return SC_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int npa_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left)
|
static int npa_finish(sc_card_t * card)
|
||||||
{
|
{
|
||||||
|
struct npa_drv_data *drv_data;
|
||||||
|
|
||||||
|
if (card) {
|
||||||
|
drv_data = card->drv_data;
|
||||||
|
if (drv_data) {
|
||||||
|
free(drv_data->ef_cardaccess);
|
||||||
|
free(drv_data->can);
|
||||||
|
free(drv_data);
|
||||||
|
}
|
||||||
|
card->drv_data = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SC_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int npa_pin_cmd_get_info(struct sc_card *card,
|
||||||
|
struct sc_pin_cmd_data *data, int *tries_left)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 pin_reference;
|
||||||
|
|
||||||
|
if (!data || data->pin_type != SC_AC_CHV || !tries_left) {
|
||||||
|
r = SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
pin_reference = data->pin_reference;
|
||||||
|
switch (data->pin_reference) {
|
||||||
|
case PACE_PIN_ID_CAN:
|
||||||
|
case PACE_PIN_ID_MRZ:
|
||||||
|
case PACE_PIN_ID_PUK:
|
||||||
|
/* usually unlimited number of retries */
|
||||||
|
*tries_left = -1;
|
||||||
|
data->pin1.max_tries = -1;
|
||||||
|
data->pin1.tries_left = -1;
|
||||||
|
r = SC_SUCCESS;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PACE_PIN_ID_PIN:
|
||||||
|
/* usually 3 tries */
|
||||||
|
*tries_left = 3;
|
||||||
|
data->pin1.max_tries = 3;
|
||||||
|
r = npa_pace_get_tries_left(card,
|
||||||
|
pin_reference, tries_left);
|
||||||
|
data->pin1.tries_left = *tries_left;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
r = SC_ERROR_OBJECT_NOT_FOUND;
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err:
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int npa_pace_verify(struct sc_card *card,
|
||||||
|
unsigned char pin_reference, struct sc_pin_cmd_pin *pin,
|
||||||
|
const unsigned char *chat, size_t chat_length, int *tries_left)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
struct establish_pace_channel_input pace_input;
|
struct establish_pace_channel_input pace_input;
|
||||||
struct establish_pace_channel_output pace_output;
|
struct establish_pace_channel_output pace_output;
|
||||||
int r;
|
|
||||||
|
|
||||||
memset(&pace_input, 0, sizeof pace_input);
|
memset(&pace_input, 0, sizeof pace_input);
|
||||||
memset(&pace_output, 0, sizeof pace_output);
|
memset(&pace_output, 0, sizeof pace_output);
|
||||||
|
if (chat) {
|
||||||
|
pace_input.chat = chat;
|
||||||
|
pace_input.chat_length = chat_length;
|
||||||
|
}
|
||||||
|
pace_input.pin_id = pin_reference;
|
||||||
|
if (pin) {
|
||||||
|
pace_input.pin = pin->data;
|
||||||
|
pace_input.pin_length = pin->len;
|
||||||
|
}
|
||||||
|
npa_get_cache(card, pace_input.pin_id, &pace_input.pin,
|
||||||
|
&pace_input.pin_length, &pace_output.ef_cardaccess,
|
||||||
|
&pace_output.ef_cardaccess_length);
|
||||||
|
|
||||||
|
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
|
||||||
|
|
||||||
|
if (tries_left) {
|
||||||
|
if (pace_output.mse_set_at_sw1 == 0x63
|
||||||
|
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) {
|
||||||
|
*tries_left = pace_output.mse_set_at_sw2 & 0x0f;
|
||||||
|
} else {
|
||||||
|
*tries_left = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* resume the PIN if needed */
|
||||||
|
if (pin_reference == PACE_PIN_ID_PIN
|
||||||
|
&& r != SC_SUCCESS
|
||||||
|
&& pace_output.mse_set_at_sw1 == 0x63
|
||||||
|
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0
|
||||||
|
&& (pace_output.mse_set_at_sw2 & 0x0f) <= UC_PIN_SUSPENDED) {
|
||||||
|
/* TODO ask for user consent when automatically resuming the PIN */
|
||||||
|
sc_log(card->ctx, "%s is suspended. Will try to resume it with %s.\n",
|
||||||
|
npa_secret_name(pin_reference), npa_secret_name(PACE_PIN_ID_CAN));
|
||||||
|
|
||||||
|
pace_input.pin_id = PACE_PIN_ID_CAN;
|
||||||
|
pace_input.pin = NULL;
|
||||||
|
pace_input.pin_length = 0;
|
||||||
|
npa_get_cache(card, pace_input.pin_id, &pace_input.pin,
|
||||||
|
&pace_input.pin_length, NULL, NULL);
|
||||||
|
|
||||||
|
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
|
||||||
|
|
||||||
|
if (r == SC_SUCCESS) {
|
||||||
|
pace_input.pin_id = pin_reference;
|
||||||
|
if (pin) {
|
||||||
|
pace_input.pin = pin->data;
|
||||||
|
pace_input.pin_length = pin->len;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = perform_pace(card, pace_input, &pace_output, EAC_TR_VERSION_2_02);
|
||||||
|
|
||||||
|
if (r == SC_SUCCESS) {
|
||||||
|
sc_log(card->ctx, "%s resumed.\n");
|
||||||
|
if (tries_left) {
|
||||||
|
*tries_left = MAX_PIN_TRIES;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (tries_left) {
|
||||||
|
if (pace_output.mse_set_at_sw1 == 0x63
|
||||||
|
&& (pace_output.mse_set_at_sw2 & 0xc0) == 0xc0) {
|
||||||
|
*tries_left = pace_output.mse_set_at_sw2 & 0x0f;
|
||||||
|
} else {
|
||||||
|
*tries_left = -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pin_reference == PACE_PIN_ID_PIN && tries_left) {
|
||||||
|
if (*tries_left == 0) {
|
||||||
|
sc_log(card->ctx, "%s is suspended and must be resumed.\n",
|
||||||
|
npa_secret_name(pin_reference));
|
||||||
|
} else if (*tries_left == 1) {
|
||||||
|
sc_log(card->ctx, "%s is blocked and must be unblocked.\n",
|
||||||
|
npa_secret_name(pin_reference));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
npa_set_cache(card, pace_input.pin_id, pace_input.pin,
|
||||||
|
pace_input.pin_length, pace_output.ef_cardaccess,
|
||||||
|
pace_output.ef_cardaccess_length);
|
||||||
|
|
||||||
|
free(pace_output.ef_cardaccess);
|
||||||
|
free(pace_output.recent_car);
|
||||||
|
free(pace_output.previous_car);
|
||||||
|
free(pace_output.id_icc);
|
||||||
|
free(pace_output.id_pcd);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int npa_standard_pin_cmd(struct sc_card *card,
|
||||||
|
struct sc_pin_cmd_data *data, int *tries_left)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
struct sc_card_driver *iso_drv;
|
||||||
|
|
||||||
|
iso_drv = sc_get_iso7816_driver();
|
||||||
|
|
||||||
|
if (!iso_drv || !iso_drv->ops || !iso_drv->ops->pin_cmd) {
|
||||||
|
r = SC_ERROR_INTERNAL;
|
||||||
|
} else {
|
||||||
|
r = iso_drv->ops->pin_cmd(card, data, tries_left);
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int npa_pin_cmd(struct sc_card *card,
|
||||||
|
struct sc_pin_cmd_data *data, int *tries_left)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
if (!data) {
|
if (!data) {
|
||||||
r = SC_ERROR_INVALID_ARGUMENTS;
|
r = SC_ERROR_INVALID_ARGUMENTS;
|
||||||
@@ -88,68 +275,83 @@ static int npa_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data, int *
|
|||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tries_left)
|
switch (data->cmd) {
|
||||||
*tries_left = -1;
|
case SC_PIN_CMD_GET_INFO:
|
||||||
|
r = npa_pin_cmd_get_info(card, data, tries_left);
|
||||||
if (data->cmd != SC_PIN_CMD_UNBLOCK
|
|
||||||
&& data->cmd != SC_PIN_CMD_VERIFY
|
|
||||||
&& data->cmd != SC_PIN_CMD_CHANGE) {
|
|
||||||
/* TODO support SC_PIN_CMD_GET_INFO */
|
|
||||||
r = SC_ERROR_NOT_SUPPORTED;
|
|
||||||
goto err;
|
|
||||||
}
|
|
||||||
|
|
||||||
pace_input.pin_id = data->pin_reference;
|
|
||||||
pace_input.pin = data->pin1.data;
|
|
||||||
pace_input.pin_length = data->pin1.len;
|
|
||||||
|
|
||||||
if (data->cmd == SC_PIN_CMD_UNBLOCK) {
|
|
||||||
/* FIXME SC_PIN_CMD_UNBLOCK is currently overloaded with the resume
|
|
||||||
* command. Iff the first secret has the length of a CAN (6) we are
|
|
||||||
* resuming the PIN with the CAN. Otherwise we will unblock the PIN
|
|
||||||
* with PUK. */
|
|
||||||
if (pace_input.pin_length == CAN_LEN) {
|
|
||||||
/* Try to resume the PIN with the CAN. */
|
|
||||||
pace_input.pin_id = PACE_PIN_ID_CAN;
|
|
||||||
r = perform_pace(card, pace_input, &pace_output, 2);
|
|
||||||
if (r != SC_SUCCESS)
|
if (r != SC_SUCCESS)
|
||||||
goto err;
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
/* Initialize pace_input for usage with the intended PIN */
|
|
||||||
pace_input.pin_id = data->pin_reference;
|
|
||||||
pace_input.pin = data->pin2.data;
|
|
||||||
pace_input.pin_length = data->pin2.len;
|
|
||||||
} else {
|
|
||||||
/* Initialize pace_input for usage with the PUK */
|
|
||||||
pace_input.pin_id = PACE_PIN_ID_PUK;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
r = perform_pace(card, pace_input, &pace_output, 2);
|
|
||||||
if (r != SC_SUCCESS)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (pace_output.mse_set_at_sw1 == 0x63) {
|
|
||||||
if ((pace_output.mse_set_at_sw2 & 0xF0) == 0xC0 && tries_left != NULL)
|
|
||||||
*tries_left = pace_output.mse_set_at_sw2 & 0x0F;
|
|
||||||
if (*tries_left > 0 && r != SC_SUCCESS)
|
|
||||||
*tries_left = *tries_left - 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (data->cmd) {
|
|
||||||
case SC_PIN_CMD_UNBLOCK:
|
case SC_PIN_CMD_UNBLOCK:
|
||||||
if (pace_input.pin_length != CAN_LEN) {
|
/* opensc-explorer unblocks the PIN by only sending
|
||||||
r = npa_unblock_pin(card);
|
* SC_PIN_CMD_UNBLOCK whereas the PKCS#15 framework first verifies
|
||||||
|
* the PUK with SC_PIN_CMD_VERIFY and then calls with
|
||||||
|
* SC_PIN_CMD_UNBLOCK.
|
||||||
|
*
|
||||||
|
* Here we determine whether the PUK has been verified or not by
|
||||||
|
* checking if an SM channel has been established. */
|
||||||
|
if (card->sm_ctx.sm_mode != SM_MODE_TRANSMIT) {
|
||||||
|
/* PUK has not yet been verified */
|
||||||
|
r = npa_pace_verify(card, PACE_PIN_ID_PUK, &(data->pin1), NULL,
|
||||||
|
0, NULL);
|
||||||
|
if (r != SC_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
}
|
||||||
|
r = npa_reset_retry_counter(card, data->pin_reference, 0,
|
||||||
|
NULL, 0);
|
||||||
|
if (r != SC_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case SC_PIN_CMD_CHANGE:
|
||||||
|
case SC_PIN_CMD_VERIFY:
|
||||||
|
switch (data->pin_reference) {
|
||||||
|
case PACE_PIN_ID_CAN:
|
||||||
|
case PACE_PIN_ID_PUK:
|
||||||
|
case PACE_PIN_ID_MRZ:
|
||||||
|
case PACE_PIN_ID_PIN:
|
||||||
|
r = npa_pace_verify(card, data->pin_reference,
|
||||||
|
&(data->pin1), NULL, 0, tries_left);
|
||||||
|
if (r != SC_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NPA_PIN_ID_ESIGN_PIN:
|
||||||
|
if (card->reader->capabilities & SC_READER_CAP_PACE_ESIGN) {
|
||||||
|
sc_log(card->ctx, "Found a comfort reader (CAT-K).\n");
|
||||||
|
sc_log(card->ctx, "Will verify cached CAN first.\n");
|
||||||
|
r = npa_pace_verify(card, PACE_PIN_ID_CAN, NULL,
|
||||||
|
esign_chat, sizeof esign_chat, tries_left);
|
||||||
|
if (r != SC_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
} else {
|
||||||
|
sc_log(card->ctx, "No comfort reader (CAT-K) found.\n");
|
||||||
|
sc_log(card->ctx, "I hope you have already performed EAC as ST...\n");
|
||||||
|
}
|
||||||
|
r = npa_standard_pin_cmd(card, data, tries_left);
|
||||||
|
if (r != SC_SUCCESS)
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
r = SC_ERROR_OBJECT_NOT_FOUND;
|
||||||
|
goto err;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data->cmd == SC_PIN_CMD_CHANGE) {
|
||||||
|
r = npa_reset_retry_counter(card, data->pin_reference, 1,
|
||||||
|
(const char *) data->pin2.data, data->pin2.len);
|
||||||
if (r != SC_SUCCESS)
|
if (r != SC_SUCCESS)
|
||||||
goto err;
|
goto err;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case SC_PIN_CMD_CHANGE:
|
|
||||||
r = npa_reset_retry_counter(card, data->pin_reference, 1,
|
default:
|
||||||
(const char *) data->pin2.data, data->pin2.len);
|
r = SC_ERROR_INTERNAL;
|
||||||
if (r != SC_SUCCESS)
|
goto err;
|
||||||
goto err;
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
err:
|
err:
|
||||||
@@ -163,6 +365,7 @@ static struct sc_card_driver *npa_get_driver(void)
|
|||||||
npa_ops = *iso_drv->ops;
|
npa_ops = *iso_drv->ops;
|
||||||
npa_ops.match_card = npa_match_card;
|
npa_ops.match_card = npa_match_card;
|
||||||
npa_ops.init = npa_init;
|
npa_ops.init = npa_init;
|
||||||
|
npa_ops.finish = npa_finish;
|
||||||
npa_ops.pin_cmd = npa_pin_cmd;
|
npa_ops.pin_cmd = npa_pin_cmd;
|
||||||
|
|
||||||
return &npa_drv;
|
return &npa_drv;
|
||||||
@@ -185,3 +388,51 @@ const char *sc_driver_version(void)
|
|||||||
* tell OpenSC that everything is fine, here. */
|
* tell OpenSC that everything is fine, here. */
|
||||||
return sc_get_version();
|
return sc_get_version();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void npa_get_cache(struct sc_card *card,
|
||||||
|
unsigned char pin_id, const unsigned char **pin, size_t *pin_length,
|
||||||
|
unsigned char **ef_cardaccess, size_t *ef_cardaccess_length)
|
||||||
|
{
|
||||||
|
struct npa_drv_data *drv_data;
|
||||||
|
if (card && card->drv_data) {
|
||||||
|
drv_data = card->drv_data;
|
||||||
|
if (pin_id == PACE_PIN_ID_CAN && pin && pin_length && !*pin) {
|
||||||
|
/* set CAN if *pin is NULL */
|
||||||
|
*pin = drv_data->can;
|
||||||
|
*pin_length = drv_data->can_length;
|
||||||
|
sc_log(card->ctx, "Using the cached CAN\n");
|
||||||
|
}
|
||||||
|
if (ef_cardaccess && ef_cardaccess_length && !*ef_cardaccess) {
|
||||||
|
/* set EF.CardAccess if *ef_cardaccess is NULL */
|
||||||
|
*ef_cardaccess = drv_data->ef_cardaccess;
|
||||||
|
*ef_cardaccess_length = drv_data->ef_cardaccess_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void npa_set_cache(struct sc_card *card,
|
||||||
|
unsigned char pin_id, const unsigned char *pin, size_t pin_length,
|
||||||
|
const unsigned char *ef_cardaccess, size_t ef_cardaccess_length)
|
||||||
|
{
|
||||||
|
unsigned char *p;
|
||||||
|
struct npa_drv_data *drv_data;
|
||||||
|
if (card && card->drv_data) {
|
||||||
|
drv_data = card->drv_data;
|
||||||
|
if (pin_id == PACE_PIN_ID_CAN && pin) {
|
||||||
|
p = realloc(drv_data->can, pin_length);
|
||||||
|
if (p) {
|
||||||
|
memcpy(p, pin, pin_length);
|
||||||
|
drv_data->can = p;
|
||||||
|
drv_data->can_length = pin_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ef_cardaccess) {
|
||||||
|
p = realloc(drv_data->ef_cardaccess, ef_cardaccess_length);
|
||||||
|
if (p) {
|
||||||
|
memcpy(p, ef_cardaccess, ef_cardaccess_length);
|
||||||
|
drv_data->ef_cardaccess = p;
|
||||||
|
drv_data->ef_cardaccess_length = ef_cardaccess_length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
58
npa/src/card-npa.h
Normal file
58
npa/src/card-npa.h
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2014 Frank Morgner
|
||||||
|
*
|
||||||
|
* This file is part of npa.
|
||||||
|
*
|
||||||
|
* npa is free software: you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU General Public License as published by the Free Software
|
||||||
|
* Foundation, either version 3 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* npa 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 General Public License for more
|
||||||
|
* details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along with
|
||||||
|
* npa. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _CARD_NPA_H
|
||||||
|
#define _CARD_NPA_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "libopensc/opensc.h"
|
||||||
|
|
||||||
|
enum {
|
||||||
|
SC_CARD_TYPE_NPA = 42000,
|
||||||
|
SC_CARD_TYPE_NPA_TEST,
|
||||||
|
SC_CARD_TYPE_NPA_ONLINE,
|
||||||
|
};
|
||||||
|
|
||||||
|
#define NPA_PIN_ID_ESIGN_PIN 0x83
|
||||||
|
|
||||||
|
const unsigned char esign_chat[] = {
|
||||||
|
0x7F, 0x4C, 0x0E,
|
||||||
|
0x06, 0x09, 0x04, 0x00, 0x7F, 0x00, 0x07, 0x03, 0x01, 0x02, 0x03,
|
||||||
|
0x53, 0x01, 0x03,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const unsigned char df_esign_aid[] = { 0xa0, 0x00, 0x00, 0x01, 0x67, 0x45, 0x53, 0x49, 0x47, 0x4e};
|
||||||
|
static const unsigned char df_esign_path[] = { 0x3f, 0x00, 0x50, 0x15, 0x1f, 0xff};
|
||||||
|
static const unsigned char ef_cardaccess_path[] = { 0x3f, 0x00, 0x01, 0x1c};
|
||||||
|
|
||||||
|
void npa_get_cache(struct sc_card *card,
|
||||||
|
unsigned char pin_id, const unsigned char **pin, size_t *pin_length,
|
||||||
|
unsigned char **ef_cardaccess, size_t *ef_cardaccess_length);
|
||||||
|
|
||||||
|
void npa_set_cache(struct sc_card *card,
|
||||||
|
unsigned char pin_id, const unsigned char *pin, size_t pin_length,
|
||||||
|
const unsigned char *ef_cardaccess, size_t ef_cardaccess_length);
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
@@ -602,6 +602,27 @@ static int npa_mse_set_at_pace(sc_card_t *card, int protocol,
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int npa_pace_get_tries_left(sc_card_t *card,
|
||||||
|
enum s_type pin_id, int *tries_left)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
u8 sw1, sw2;
|
||||||
|
|
||||||
|
if (tries_left) {
|
||||||
|
r = npa_mse_set_at_pace(card, 0, pin_id, 0, &sw1, &sw2);
|
||||||
|
|
||||||
|
if (r > 0 && (sw1 == 0x63) && ((sw2 & 0xc0) == 0xc0)) {
|
||||||
|
*tries_left = sw2 & 0x0f;
|
||||||
|
} else {
|
||||||
|
*tries_left = -1;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
r = SC_ERROR_INVALID_ARGUMENTS;
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#define ISO_GENERAL_AUTHENTICATE 0x86
|
#define ISO_GENERAL_AUTHENTICATE 0x86
|
||||||
#define ISO_COMMAND_CHAINING 0x10
|
#define ISO_COMMAND_CHAINING 0x10
|
||||||
|
|||||||
@@ -98,6 +98,11 @@ extern "C" {
|
|||||||
#define CAN_LEN 6
|
#define CAN_LEN 6
|
||||||
/** @brief Minimum length of MRZ */
|
/** @brief Minimum length of MRZ */
|
||||||
#define MAX_MRZ_LEN 128
|
#define MAX_MRZ_LEN 128
|
||||||
|
/** @brief Number of retries for PIN */
|
||||||
|
#define MAX_PIN_TRIES 3
|
||||||
|
/** @brief Usage counter of PIN in suspended state */
|
||||||
|
#define UC_PIN_SUSPENDED 1
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Names the type of the PACE secret
|
* @brief Names the type of the PACE secret
|
||||||
@@ -199,6 +204,18 @@ int perform_chip_authentication(sc_card_t *card);
|
|||||||
int npa_reset_retry_counter(sc_card_t *card,
|
int npa_reset_retry_counter(sc_card_t *card,
|
||||||
enum s_type pin_id, int ask_for_secret,
|
enum s_type pin_id, int ask_for_secret,
|
||||||
const char *new, size_t new_len);
|
const char *new, size_t new_len);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sends an MSE:Set AT to determine the number of remaining tries
|
||||||
|
*
|
||||||
|
* @param[in] card
|
||||||
|
* @param[in] pin_id Type of secret (usually PIN or CAN). You may use <tt>enum s_type</tt> from \c <openssl/pace.h>.
|
||||||
|
* @param[in,out] tries_left Tries left or -1 if no specific number has been returned by the card (e.g. when there is no limit in retries).
|
||||||
|
*
|
||||||
|
* @return \c SC_SUCCESS or error code if an error occurred
|
||||||
|
*/
|
||||||
|
int npa_pace_get_tries_left(sc_card_t *card,
|
||||||
|
enum s_type pin_id, int *tries_left);
|
||||||
/**
|
/**
|
||||||
* @brief Send APDU to unblock the PIN
|
* @brief Send APDU to unblock the PIN
|
||||||
*
|
*
|
||||||
|
|||||||
Reference in New Issue
Block a user