Improved PIN change function:

- Uses PIN padding from merged policy
- Improved PIN-pad logic and merged here from separate function
This commit is contained in:
Zoltan Kelemen 2020-07-01 09:31:01 +02:00 committed by Frank Morgner
parent 5ae488c1b9
commit 79e81eeef0
1 changed files with 52 additions and 89 deletions

View File

@ -2029,62 +2029,6 @@ iasecc_pin_verify(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries
}
static int
iasecc_chv_change_pinpad(struct sc_card *card, unsigned reference, int *tries_left)
{
struct sc_context *ctx = card->ctx;
struct sc_pin_cmd_data pin_cmd;
unsigned char pin1_data[0x100], pin2_data[0x100];
int rv;
LOG_FUNC_CALLED(ctx);
sc_log(ctx, "CHV PINPAD PIN reference %i", reference);
memset(pin1_data, 0xFF, sizeof(pin1_data));
memset(pin2_data, 0xFF, sizeof(pin2_data));
if (!card->reader || !card->reader->ops || !card->reader->ops->perform_verify) {
sc_log(ctx, "Reader not ready for PIN PAD");
LOG_FUNC_RETURN(ctx, SC_ERROR_READER);
}
memset(&pin_cmd, 0, sizeof(pin_cmd));
pin_cmd.pin_type = SC_AC_CHV;
pin_cmd.pin_reference = reference;
pin_cmd.cmd = SC_PIN_CMD_CHANGE;
pin_cmd.flags |= SC_PIN_CMD_USE_PINPAD;
rv = iasecc_pin_get_policy(card, &pin_cmd);
LOG_TEST_RET(ctx, rv, "Get 'PIN policy' error");
/* Some pin-pads do not support mode with Lc=0.
* Give them a chance to work with some cards.
*/
if ((pin_cmd.pin1.min_length == pin_cmd.pin1.stored_length) && (pin_cmd.pin1.max_length == pin_cmd.pin1.min_length))
pin_cmd.pin1.len = pin_cmd.pin1.stored_length;
else
pin_cmd.pin1.len = 0;
pin_cmd.pin1.length_offset = 5;
pin_cmd.pin1.data = pin1_data;
memcpy(&pin_cmd.pin2, &pin_cmd.pin1, sizeof(pin_cmd.pin1));
pin_cmd.pin2.data = pin2_data;
sc_log(ctx,
"PIN1 max/min/stored: %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u",
pin_cmd.pin1.max_length, pin_cmd.pin1.min_length,
pin_cmd.pin1.stored_length);
sc_log(ctx,
"PIN2 max/min/stored: %"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u/%"SC_FORMAT_LEN_SIZE_T"u",
pin_cmd.pin2.max_length, pin_cmd.pin2.min_length,
pin_cmd.pin2.stored_length);
rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left);
LOG_FUNC_RETURN(ctx, rv);
}
#if 0
static int
iasecc_chv_set_pinpad(struct sc_card *card, unsigned char reference)
@ -2404,54 +2348,73 @@ iasecc_keyset_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tr
}
/*
* The PIN change function can handle different PIN-pad input combinations for the old and new
* PINs:
* OLD PIN: NEW PIN: DESCRIPTION:
* Available Available No input.
* Available Absent Only new PIN is input.
* Absent Available Both PINs are input (due to limitations in IAS-ECC)
* Absent Absent Both PINs are input.
*/
static int
iasecc_pin_change(struct sc_card *card, struct sc_pin_cmd_data *data, int *tries_left)
{
struct sc_context *ctx = card->ctx;
struct sc_apdu apdu;
unsigned reference = data->pin_reference;
unsigned char pin_data[0x100];
struct sc_pin_cmd_data pin_cmd;
struct iasecc_pin_policy policy;
int rv;
LOG_FUNC_CALLED(ctx);
sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%i/%i)", reference, data->pin_type, data->pin1.len, data->pin2.len);
sc_log(ctx, "Change PIN(ref:%i,type:0x%X,lengths:%i/%i)",
data->pin_reference, data->pin_type, data->pin1.len, data->pin2.len);
if ((card->reader->capabilities & SC_READER_CAP_PIN_PAD)) {
if (!data->pin1.data && !data->pin1.len && !data->pin2.data && !data->pin2.len) {
rv = iasecc_chv_change_pinpad(card, reference, tries_left);
sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) chv_change_pinpad returned %i", rv);
LOG_FUNC_RETURN(ctx, rv);
}
if (data->pin_type != SC_AC_CHV) {
sc_log(ctx, "Can not change non-CHV PINs");
LOG_FUNC_RETURN(ctx, SC_ERROR_NOT_SUPPORTED);
}
if (!data->pin1.data && data->pin1.len)
LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN1 arguments");
/*
* Verify the original PIN. This would normally not be needed since it is implicitly done
* by the card when executing a PIN change command. But we must go through our verification
* function in order to handle secure messaging setup, if enabled for the PIN. The
* verification is skipped for PIN-pads (which do not work with SM anyway), to avoid the
* user having to enter the PIN twice.
*/
pin_cmd = *data;
pin_cmd.cmd = SC_PIN_CMD_VERIFY;
if (!data->pin2.data && data->pin2.len)
LOG_TEST_RET(ctx, SC_ERROR_INVALID_ARGUMENTS, "Invalid PIN2 arguments");
rv = iasecc_pin_merge_policy(card, &pin_cmd, &pin_cmd.pin1, &policy);
LOG_TEST_RET(ctx, rv, "Failed to update PIN1 info");
rv = iasecc_pin_verify(card, data->pin_type, reference, data->pin1.data, data->pin1.len, tries_left);
sc_log(ctx, "iasecc_pin_cmd(SC_PIN_CMD_CHANGE) pin_verify returned %i", rv);
LOG_TEST_RET(ctx, rv, "PIN verification error");
if (!(pin_cmd.flags & SC_PIN_CMD_USE_PINPAD)) {
rv = iasecc_chv_verify(card, &pin_cmd, policy.scbs, tries_left);
LOG_TEST_RET(ctx, rv, "PIN CHV verification error");
}
if ((unsigned)(data->pin1.len + data->pin2.len) > sizeof(pin_data))
LOG_TEST_RET(ctx, SC_ERROR_BUFFER_TOO_SMALL, "Buffer too small for the 'Change PIN' data");
/*
* To keep things simple, assume that we can use the same PIN parameters for the new PIN as
* for the old one, ignoring the ones specified by the caller, with the exception of the
* PIN prompt and the PIN data itself. Note that the old PIN is re-verified since the
* IAS-ECC specification has no implicit verification for the PIN change command. This also
* forces us to always use PIN-pad for the second PIN if the first one was input on a
* PIN-pad.
*/
pin_cmd.cmd = SC_PIN_CMD_CHANGE;
pin_cmd.pin2 = pin_cmd.pin1;
pin_cmd.pin2.prompt = data->pin2.prompt;
if (pin_cmd.flags & SC_PIN_CMD_USE_PINPAD) {
pin_cmd.pin2.data = NULL;
pin_cmd.pin2.len = 0;
} else {
pin_cmd.pin2.data = data->pin2.data;
pin_cmd.pin2.len = data->pin2.len;
}
if (data->pin1.data)
memcpy(pin_data, data->pin1.data, data->pin1.len);
if (data->pin2.data)
memcpy(pin_data + data->pin1.len, data->pin2.data, data->pin2.len);
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, 0, reference);
apdu.data = pin_data;
apdu.datalen = data->pin1.len + data->pin2.len;
apdu.lc = apdu.datalen;
rv = sc_transmit_apdu(card, &apdu);
LOG_TEST_RET(ctx, rv, "APDU transmit failed");
rv = sc_check_sw(card, apdu.sw1, apdu.sw2);
LOG_TEST_RET(ctx, rv, "PIN cmd failed");
rv = iasecc_check_update_pin(&pin_cmd, &pin_cmd.pin2);
LOG_TEST_RET(ctx, rv, "Invalid PIN2");
rv = iso_ops->pin_cmd(card, &pin_cmd, tries_left);
LOG_FUNC_RETURN(ctx, rv);
}