- Implemented new PIN verify/change/unblock framework. All PIN operations

are routed through sc_pin_cmd(), which builds the APDU and either passes
  it to the card directly, or to the card reader along with a request to
  read the PIN(s) from the reader's keypad.

  Currently, entering PIN in the standard way (i.e. via the application)
  should still work - I have verified GPK and eToken; Cryptoflex verify
  should work as well. Anything else needs additional testing, and support
  for keypad input in particular (I cannot test this at the moment for
  lack of a suitable reader).


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@811 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
okir 2002-12-23 18:47:27 +00:00
parent 604e9cd318
commit 9acca0e724
17 changed files with 791 additions and 352 deletions

View File

@ -26,7 +26,7 @@ libopensc_la_SOURCES = sc.c ctx.c module.c asn1.c log.c base64.c \
pkcs15.c pkcs15-cert.c pkcs15-data.c pkcs15-pin.c \
pkcs15-prkey.c pkcs15-pubkey.c pkcs15-sec.c \
pkcs15-wrap.c pkcs15-algo.c \
pkcs15-cache.c $(PCSC_SRC) reader-ctapi.c \
pkcs15-cache.c $(PCSC_SRC) reader-ctapi.c ctbcs.c \
card-setcos.c card-miocos.c card-flex.c card-gpk.c \
card-etoken.c card-tcos.c card-emv.c card-default.c \
card-mcrd.c

View File

@ -538,18 +538,6 @@ static int etoken_read_binary(struct sc_card *card,
return total? total : n;
}
/*
* The 0x80 thing tells the card it's okay to search parent
* directories as well for the referenced object.
* Unfortunately, it doesn't seem to work without this flag :-/
*/
static int
etoken_verify(struct sc_card *card, unsigned int type, int ref,
const u8 *pin, size_t pin_len, int *tries_left)
{
return iso_ops->verify(card, type, ref|0x80, pin, pin_len, tries_left);
}
/*
* Restore the indicated SE
*/
@ -789,6 +777,25 @@ etoken_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr)
return SC_ERROR_NOT_SUPPORTED;
}
/*
* The 0x80 thing tells the card it's okay to search parent
* directories as well for the referenced object.
* Unfortunately, it doesn't seem to work without this flag :-/
*/
static int
etoken_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
data->flags |= SC_PIN_CMD_NEED_PADDING;
data->pin_reference |= 0x80;
/* FIXME: the following values depend on what pin length was
* used when creating the BS objects */
data->pin1.max_length = 8;
data->pin2.max_length = 8;
return iso_ops->pin_cmd(card, data, tries_left);
}
/* eToken R2 supports WRITE_BINARY, PRO Tokens support UPDATE_BINARY */
static const struct sc_card_driver * sc_get_driver(void)
@ -803,7 +810,6 @@ static const struct sc_card_driver * sc_get_driver(void)
etoken_ops.create_file = etoken_create_file;
etoken_ops.update_binary = etoken_update_binary;
etoken_ops.read_binary = etoken_read_binary;
etoken_ops.verify = etoken_verify;
etoken_ops.set_security_env = etoken_set_security_env;
etoken_ops.restore_security_env = etoken_restore_security_env;
etoken_ops.compute_signature = etoken_compute_signature;
@ -811,6 +817,7 @@ static const struct sc_card_driver * sc_get_driver(void)
etoken_ops.list_files = etoken_list_files;
etoken_ops.check_sw = etoken_check_sw;
etoken_ops.card_ctl = etoken_card_ctl;
etoken_ops.pin_cmd = etoken_pin_cmd;
return &etoken_drv;
}

View File

@ -84,6 +84,7 @@ struct flex_private_data {
};
static struct sc_card_operations flex_ops;
static struct sc_card_operations *iso_ops;
static const struct sc_card_driver flex_drv = {
"Schlumberger Multiflex/Cryptoflex",
"flex",
@ -752,44 +753,6 @@ static int flex_compute_signature(struct sc_card *card, const u8 *data,
return apdu.resplen;
}
static int flex_verify(struct sc_card *card, unsigned int type, int ref,
const u8 *buf, size_t buflen, int *tries_left)
{
struct sc_apdu apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
int cla, ins;
if (buflen >= SC_MAX_APDU_BUFFER_SIZE)
return SC_ERROR_INVALID_ARGUMENTS;
switch (type) {
case SC_AC_CHV:
cla = 0xC0;
ins = 0x20;
break;
case SC_AC_AUT:
cla = 0xF0;
ins = 0x2A;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, ins, 0, ref);
memcpy(sbuf, buf, buflen);
apdu.cla = cla;
apdu.lc = buflen;
apdu.datalen = buflen;
apdu.data = sbuf;
apdu.resplen = 0;
apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, buflen);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 == 0x63)
return SC_ERROR_PIN_CODE_INCORRECT;
return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
/* Return the default AAK for this type of card */
static int flex_get_default_key(struct sc_card *card,
struct sc_cardctl_default_key *data)
@ -829,11 +792,69 @@ static int flex_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr)
return SC_ERROR_NOT_SUPPORTED;
}
static int flex_build_verify_apdu(struct sc_card *card, struct sc_apdu *apdu,
struct sc_pin_cmd_data *data)
{
static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, len;
int cla, ins;
switch (data->pin_type) {
case SC_AC_CHV:
cla = 0xC0;
ins = 0x20;
break;
case SC_AC_AUT:
/* AUT keys cannot be entered through terminal */
if (data->flags & SC_PIN_CMD_USE_PINPAD)
return SC_ERROR_INVALID_ARGUMENTS;
cla = 0xF0;
ins = 0x2A;
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
/* Copy the PIN, with padding */
if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, 1)) < 0)
return r;
len = r;
sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT, ins, 0, data->pin_reference);
apdu->cla = cla;
apdu->data = sbuf;
apdu->datalen = len;
apdu->lc = len;
apdu->sensitive = 1;
return 0;
}
static int flex_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
sc_apdu_t apdu;
int r;
if (data->cmd == SC_PIN_CMD_VERIFY) {
r = flex_build_verify_apdu(card, &apdu, data);
if (r < 0)
return r;
data->apdu = &apdu;
}
/* According to the Cryptoflex documentation, the card
* does not return the number of attempts left using
* the 63C0xx convention, hence we don't pass the
* tries_left pointer. */
return iso_ops->pin_cmd(card, data, NULL);
}
static const struct sc_card_driver * sc_get_driver(void)
{
const struct sc_card_driver *iso_drv = sc_get_iso7816_driver();
if (iso_ops == NULL)
iso_ops = sc_get_iso7816_driver()->ops;
flex_ops = *iso_drv->ops;
flex_ops = *iso_ops;
flex_ops.match_card = flex_match_card;
flex_ops.init = flex_init;
flex_ops.finish = flex_finish;
@ -841,11 +862,11 @@ static const struct sc_card_driver * sc_get_driver(void)
flex_ops.list_files = flex_list_files;
flex_ops.delete_file = flex_delete_file;
flex_ops.create_file = flex_create_file;
flex_ops.verify = flex_verify;
flex_ops.card_ctl = flex_card_ctl;
flex_ops.set_security_env = flex_set_security_env;
flex_ops.restore_security_env = flex_restore_security_env;
flex_ops.compute_signature = flex_compute_signature;
flex_ops.pin_cmd = flex_pin_cmd;
return &flex_drv;
}

View File

@ -1025,158 +1025,6 @@ gpk_select_key(struct sc_card *card, int key_sfi, const u8 *buf, size_t buflen)
return r;
}
/*
* Verify a PIN
* XXX Checking for a PIN from the global EFsc is quite hairy,
* because we can do this only when the MF is selected.
* So, simply don't do this :-)
*/
static int
gpk_verify_pin(struct sc_card *card, int ref,
const u8 *pin, size_t pinlen, int *tries_left)
{
u8 buffer[8];
struct sc_apdu apdu;
int r;
SC_FUNC_CALLED(card->ctx, 1);
if (pinlen > 8)
return SC_ERROR_INVALID_PIN_LENGTH;
/* Copy PIN, 0-padded */
memset(buffer, 0, sizeof(buffer));
memcpy(buffer, pin, pinlen);
/* XXX deal with secure messaging here */
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x00;
apdu.ins = 0x20;
apdu.p1 = 0x00;
apdu.p2 = ref & 7;
apdu.lc = 8;
apdu.datalen = 8;
apdu.data = buffer;
apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
/* Special case: extract tries_left */
if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) {
if (tries_left)
*tries_left = apdu.sw2 & 0xF;
return SC_ERROR_PIN_CODE_INCORRECT;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
/*
* Verify key (for external auth/secure messaging) or PIN
* presented by the user
*/
static int
gpk_verify(struct sc_card *card, unsigned int type, int ref,
const u8 *buf, size_t buflen, int *tries_left)
{
if (tries_left)
*tries_left = -1;
switch (type) {
case SC_AC_PRO:
return gpk_select_key(card, ref, buf, buflen);
case SC_AC_CHV:
return gpk_verify_pin(card, ref, buf, buflen, tries_left);
}
return SC_ERROR_INVALID_ARGUMENTS;
}
/*
* Change secret code. This is used by reset_retry_counter and
* change_reference_data
*/
static int
gpk_set_secret_code(struct sc_card *card, unsigned int mode,
unsigned int type, int ref,
const u8 *puk, size_t puklen,
const u8 *pin, size_t pinlen,
int *tries_left)
{
struct sc_apdu apdu;
u8 data[8];
unsigned int n;
int r;
if (card->ctx->debug)
debug(card->ctx, "gpk_set_secret_code(mode=%d, ref=%d)\n",
mode, ref);
if (type != SC_AC_CHV || !puk || !puklen)
return SC_ERROR_INVALID_ARGUMENTS;
memset(&apdu, 0, sizeof(apdu));
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.cla = 0x80;
apdu.ins = 0x24;
apdu.p1 = mode;
apdu.p2 = ref & 7;
apdu.lc = 8;
apdu.data= data;
apdu.datalen = 8;
apdu.sensitive = 1;
memset(data, 0, sizeof(data));
for (n = 0; n < 8 && n < puklen; n += 2)
data[n >> 1] = (puk[n] << 4) | (puk[n+1] & 0xf);
for (n = 0; n < 8 && n < pinlen; n += 2)
data[4 + (n >> 1)] = (pin[n] << 4) | (pin[n+1] & 0xf);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
/* Special case: extract tries_left */
if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) {
if (tries_left)
*tries_left = apdu.sw2 & 0xF;
return SC_ERROR_PIN_CODE_INCORRECT;
}
r = sc_check_sw(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
return r;
}
/*
* Unblock the CHV
*/
static int
gpk_reset_retry_counter(struct sc_card *card,
unsigned int type, int ref,
const u8 *puk, size_t puklen,
const u8 *pin, size_t pinlen)
{
return gpk_set_secret_code(card, 0x01, type, ref,
puk, puklen, pin, pinlen, NULL);
}
/*
* Change the PIN
*/
static int
gpk_change_reference_data(struct sc_card *card,
unsigned int type, int ref,
const u8 *puk, size_t puklen,
const u8 *pin, size_t pinlen,
int *tries_left)
{
return gpk_set_secret_code(card, 0x00, type, ref,
puk, puklen, pin, pinlen, tries_left);
}
/*
* Select a security environment (Set Crypto Context in GPK docs).
* When we get here, the PK file has already been selected.
@ -1831,6 +1679,82 @@ gpk_card_ctl(struct sc_card *card, unsigned long cmd, void *ptr)
return SC_ERROR_NOT_SUPPORTED;
}
static int
gpk_build_pin_apdu(sc_card_t *card, sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
{
static u8 sbuf[8];
int r, ins, p1;
if (data->pin_type != SC_AC_CHV)
return SC_ERROR_INVALID_ARGUMENTS;
/* XXX deal with secure messaging here */
memset(apdu, 0, sizeof(*apdu));
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
/* Copy PIN to buffer and pad */
r = sc_build_pin(sbuf, 8, &data->pin1, 1);
if (r < 0)
return r;
ins = 0x20;
p1 = 0x00;
break;
case SC_PIN_CMD_CHANGE:
case SC_PIN_CMD_UNBLOCK:
/* Copy PINs to buffer, BCD-encoded, and pad */
data->pin1.encoding = SC_PIN_ENCODING_BCD;
data->pin1.max_length = 8;
data->pin2.encoding = SC_PIN_ENCODING_BCD;
data->pin2.max_length = 8;
data->pin2.offset = 4;
if ((r = sc_build_pin(sbuf, 4, &data->pin1, 1)) < 0
|| (r = sc_build_pin(sbuf + 4, 4, &data->pin2, 1)) < 0)
return r;
ins = 0x24;
p1 = (data->cmd == SC_PIN_CMD_CHANGE)? 0x00 : 0x01;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
apdu->cse = SC_APDU_CASE_3_SHORT;
apdu->cla = 0x00;
apdu->ins = ins;
apdu->p1 = p1;
apdu->p2 = data->pin_reference & 7;
apdu->lc = 8;
apdu->datalen = 8;
apdu->data = sbuf;
apdu->sensitive = 1;
return 0;
}
static int
gpk_pin_cmd(sc_card_t *card, struct sc_pin_cmd_data *data, int *tries_left)
{
struct sc_apdu apdu;
int r;
/* Special case - External Authenticate */
if (data->cmd == SC_PIN_CMD_VERIFY
&& data->pin_type == SC_AC_PRO)
return gpk_select_key(card,
data->pin_reference,
data->pin1.data,
data->pin1.len);
r = gpk_build_pin_apdu(card, &apdu, data);
if (r < 0)
return r;
data->apdu = &apdu;
return iso_ops->pin_cmd(card, data, tries_left);
}
/*
* Initialize the driver struct
*/
@ -1851,7 +1775,6 @@ sc_get_driver()
gpk_ops.read_binary = gpk_read_binary;
gpk_ops.write_binary = gpk_write_binary;
gpk_ops.update_binary = gpk_update_binary;
gpk_ops.verify = gpk_verify;
gpk_ops.create_file = gpk_create_file;
/* gpk_ops.check_sw = gpk_check_sw; */
gpk_ops.card_ctl = gpk_card_ctl;
@ -1859,8 +1782,7 @@ sc_get_driver()
gpk_ops.restore_security_env= gpk_restore_security_env;
gpk_ops.compute_signature= gpk_compute_signature;
gpk_ops.decipher = gpk_decipher;
gpk_ops.reset_retry_counter = gpk_reset_retry_counter;
gpk_ops.change_reference_data = gpk_change_reference_data;
gpk_ops.pin_cmd = gpk_pin_cmd;
}
return &gpk_drv;
}

View File

@ -1155,7 +1155,7 @@ static const struct sc_card_driver * sc_get_driver(void)
mcrd_ops.select_file = mcrd_select_file;
mcrd_ops.set_security_env = mcrd_set_security_env;
mcrd_ops.compute_signature = mcrd_compute_signature;
return &mcrd_drv;
}

View File

@ -70,7 +70,7 @@ static int sc_check_apdu(struct sc_context *ctx, const struct sc_apdu *apdu)
break;
case SC_APDU_CASE_4_SHORT:
if (apdu->datalen == 0 || apdu->data == NULL) {
error(ctx, "Case 3 APDU with no data supplied\n");
error(ctx, "Case 4 APDU with no data supplied\n");
SC_FUNC_RETURN(ctx, 4, SC_ERROR_INVALID_ARGUMENTS);
}
if (apdu->le == 0) {
@ -151,7 +151,8 @@ static int sc_transceive(struct sc_card *card, struct sc_apdu *apdu)
apdu->sensitive ? ", sensitive" : "", buf);
}
r = card->reader->ops->transmit(card->reader, card->slot, sbuf,
sendsize, rbuf, &recvsize);
sendsize, rbuf, &recvsize,
apdu->control);
if (apdu->sensitive)
memset(sbuf, 0, sendsize);
SC_TEST_RET(card->ctx, r, "Unable to transmit");

207
src/libopensc/ctbcs.c Normal file
View File

@ -0,0 +1,207 @@
/*
* ctbcs.c: Extended CTBCS commands, used for pcsc and ct-api readers
*
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "internal.h"
#include "log.h"
#include "ctbcs.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
static void
ctbcs_init_apdu(sc_apdu_t *apdu, int cse, int ins, int p1, int p2)
{
memset(apdu, 0, sizeof(*apdu));
apdu->cse = cse;
apdu->cla = 0x20;
apdu->ins = ins;
apdu->p1 = p1;
apdu->p2 = p2;
apdu->control = 1;
}
int
ctbcs_build_input_apdu(sc_apdu_t *apdu, int echo, const char *prompt,
u8 *rbuf, size_t rbuflen)
{
ctbcs_init_apdu(apdu, SC_APDU_CASE_2_SHORT,
CTBCS_INS_INPUT,
CTBCS_P1_KEYPAD,
echo? CTBCS_P2_INPUT_ECHO : CTBCS_P2_INPUT_ASTERISKS);
if (prompt && *prompt) {
apdu->cse = SC_APDU_CASE_4_SHORT;
apdu->data = prompt;
apdu->lc = apdu->datalen = strlen(prompt);
}
apdu->le = apdu->resplen = rbuflen;
apdu->resp = rbuf;
return 0;
}
int
ctbcs_build_output_apdu(sc_apdu_t *apdu, const char *message)
{
ctbcs_init_apdu(apdu,
SC_APDU_CASE_3_SHORT,
CTBCS_INS_INPUT,
CTBCS_P1_DISPLAY,
0);
if (!message || !*message)
message = " ";
apdu->lc = apdu->datalen = strlen(message);
return 0;
}
int
ctbcs_build_perform_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
{
const char *prompt;
size_t buflen, count = 0, j = 0, len;
static u8 buf[254];
u8 control;
ctbcs_init_apdu(apdu,
SC_APDU_CASE_3_SHORT,
CTBCS_INS_PERFORM_VERIFICATION,
CTBCS_P1_KEYPAD,
0);
buflen = sizeof(buf);
prompt = data->pin1.prompt;
if (prompt && *prompt) {
len = strlen(prompt);
if (count + len + 2 > buflen || len > 255)
return SC_ERROR_BUFFER_TOO_SMALL;
buf[count++] = CTBCS_TAG_PROMPT;
buf[count++] = len;
memcpy(buf + count, prompt, len);
count += len;
}
/* card apdu must be last in packet */
if (!data->apdu)
return SC_ERROR_INTERNAL;
if (count + 7 > buflen)
return SC_ERROR_BUFFER_TOO_SMALL;
j = count;
buf[j++] = CTBCS_TAG_VERIFY_CMD;
buf[j++] = 0x00;
/* Control byte - length of PIN, and encoding */
control = 0x00;
if (data->pin1.encoding == SC_PIN_ENCODING_ASCII)
control |= CTBCS_PIN_CONTROL_ENCODE_ASCII;
else if (data->pin1.encoding != SC_PIN_ENCODING_BCD)
return SC_ERROR_INVALID_ARGUMENTS;
if (data->pin1.min_length == data->pin1.max_length)
control |= data->pin1.min_length << CTBCS_PIN_CONTROL_LEN_SHIFT;
buf[j++] = control;
buf[j++] = data->pin1.offset;
buf[j++] = data->apdu->cla;
buf[j++] = data->apdu->ins;
buf[j++] = data->apdu->p1;
buf[j++] = data->apdu->p2;
if (data->flags & SC_PIN_CMD_NEED_PADDING) {
len = data->pin1.pad_length;
if (j + len > buflen || len > 256)
return SC_ERROR_BUFFER_TOO_SMALL;
buf[j++] = len;
memset(buf+j, data->pin1.pad_char, len);
j += len;
}
buf[count+1] = j - count - 2;
count = j;
apdu->lc = apdu->datalen = count;
apdu->data = buf;
return 0;
}
int
ctbcs_build_modify_verification_apdu(sc_apdu_t *apdu, struct sc_pin_cmd_data *data)
{
/* to be implemented */
return SC_ERROR_NOT_SUPPORTED;
}
int
ctbcs_pin_cmd(struct sc_reader *reader, sc_slot_info_t *slot,
struct sc_pin_cmd_data *data)
{
struct sc_card dummy_card, *card;
struct sc_apdu apdu;
int r;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
r = ctbcs_build_perform_verification_apdu(&apdu, data);
break;
case SC_PIN_CMD_CHANGE:
case SC_PIN_CMD_UNBLOCK:
r = ctbcs_build_modify_verification_apdu(&apdu, data);
break;
default:
error(reader->ctx, "unknown pin command %d", data->cmd);
return SC_ERROR_NOT_SUPPORTED;
}
memset(&dummy_card, 0, sizeof(dummy_card));
dummy_card.reader = reader;
dummy_card.slot = slot;
dummy_card.ctx = reader->ctx;
card = &dummy_card;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
/* Check CTBCS status word */
switch (((unsigned int) apdu.sw1 << 8) | apdu.sw2) {
case 0x9000:
r = 0;
break;
case 0x6400: /* Input timed out */
r = SC_ERROR_KEYPAD_TIMEOUT;
break;
case 0x6401: /* Input cancelled */
r = SC_ERROR_KEYPAD_CANCELLED;
break;
case 0x6402: /* PINs did not match */
r = SC_ERROR_KEYPAD_PIN_MISMATCH;
break;
case 0x6700: /* message too long */
r = SC_ERROR_KEYPAD_MSG_TOO_LONG;
break;
default:
r = SC_ERROR_CARD_CMD_FAILED;
break;
}
SC_TEST_RET(card->ctx, r, "PIN command failed");
return 0;
}

View File

@ -42,6 +42,14 @@
#define CTBCS_INS_STATUS 0x13 /* Get reader status */
#define CTBCS_INS_EJECT 0x15 /* Eject ICC */
/*
* Additional CT-BCS commands
*/
#define CTBCS_INS_INPUT 0x16 /* Input from pin pad */
#define CTBCS_INS_OUTPUT 0x17 /* Output to pad pad display */
#define CTBCS_INS_PERFORM_VERIFICATION 0x18 /* Verify PIN from pin pad */
#define CTBCS_INS_MODIFY_VERIFICATION 0x19 /* Perform a change/unblock PIN op */
/*
* P1 parameter: functional units
*/
@ -84,6 +92,26 @@
#define CTBCS_P2_STATUS_MANUFACTURER 0x46 /* Return manufacturer DO */
#define CTBCS_P2_STATUS_ICC 0x80 /* Return ICC DO */
/*
* P2 parameter for Input
*/
#define CTBCS_P2_INPUT_ECHO 0x01 /* Echo input on display */
#define CTBCS_P2_INPUT_ASTERISKS 0x02 /* Echo input as asterisks */
/*
* Tags for paramaters to input, output et al.
*/
#define CTBCS_TAG_PROMPT 0x50
#define CTBCS_TAG_VERIFY_CMD 0x52
#define CTBCS_TAG_TIMEOUT 0x80
/*
* PIN command control flags
*/
#define CTBCS_PIN_CONTROL_LEN_SHIFT 4
#define CTBCS_PIN_CONTROL_LEN_MASK 0x0F
#define CTBCS_PIN_CONTROL_ENCODE_ASCII 0x01
/*
* General return codes
*/
@ -147,5 +175,11 @@
#define CTBCS_DATA_STATUS_CARD 0x01 /* Card present */
#define CTBCS_DATA_STATUS_CARD_CONNECT 0x05 /* Card present */
/*
* Functions for building CTBCS commands
*/
int ctbcs_pin_cmd(struct sc_reader *, sc_slot_info_t *, struct sc_pin_cmd_data *);
#endif /* _CTBCS_ */

View File

@ -34,6 +34,10 @@ const char *sc_strerror(int error)
"Card removed",
"Card reset",
"Transmit failed",
"Timed out while waiting for input (keypad)",
"Input operation canelled (keypad)",
"The two PINs did not match (keypad)",
"Message too long (keypad)",
};
const int rdr_base = -SC_ERROR_READER;
const char *card_errors[] = {

View File

@ -37,6 +37,10 @@ extern "C" {
#define SC_ERROR_CARD_REMOVED -1105
#define SC_ERROR_CARD_RESET -1106
#define SC_ERROR_TRANSMIT_FAILED -1107
#define SC_ERROR_KEYPAD_TIMEOUT -1108
#define SC_ERROR_KEYPAD_CANCELLED -1109
#define SC_ERROR_KEYPAD_PIN_MISMATCH -1110
#define SC_ERROR_KEYPAD_MSG_TOO_LONG -1111
/* Resulting from a card command or related to the card*/
#define SC_ERROR_CARD_CMD_FAILED -1200

View File

@ -586,40 +586,6 @@ static int iso7816_delete_file(struct sc_card *card, const struct sc_path *path)
return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
static int iso7816_verify(struct sc_card *card, unsigned int type, int ref,
const u8 *pin, size_t pinlen, int *tries_left)
{
struct sc_apdu apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
if (pinlen >= SC_MAX_APDU_BUFFER_SIZE)
return SC_ERROR_INVALID_ARGUMENTS;
switch (type) {
case SC_AC_CHV:
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x20, 0, ref);
memcpy(sbuf, pin, pinlen);
apdu.lc = pinlen;
apdu.datalen = pinlen;
apdu.data = sbuf;
apdu.resplen = 0;
apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, pinlen);
SC_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;
}
return sc_check_sw(card, apdu.sw1, apdu.sw2);
}
static int iso7816_set_security_env(struct sc_card *card,
const struct sc_security_env *env,
int se_num)
@ -793,85 +759,126 @@ static int iso7816_decipher(struct sc_card *card,
SC_FUNC_RETURN(card->ctx, 2, sc_check_sw(card, apdu.sw1, apdu.sw2));
}
static int iso7816_change_reference_data(struct sc_card *card, unsigned int type,
int ref, const u8 *old, size_t oldlen,
const u8 *_new, size_t newlen,
int *tries_left)
static int iso7816_build_pin_apdu(struct sc_card *card,
struct sc_apdu *apdu,
struct sc_pin_cmd_data *data)
{
struct sc_apdu apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, p1 = 0, len = oldlen + newlen;
if (len >= SC_MAX_APDU_BUFFER_SIZE)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
switch (type) {
static u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, len, pad = 0, ins, p1 = 0;
switch (data->pin_type) {
case SC_AC_CHV:
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
if (oldlen == 0)
p1 = 1;
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x24, p1, ref);
memcpy(sbuf, old, oldlen);
memcpy(sbuf + oldlen, _new, newlen);
apdu.lc = len;
apdu.datalen = len;
apdu.data = sbuf;
apdu.resplen = 0;
apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, len);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
if (apdu.sw1 == 0x63 && (apdu.sw2 & 0xF0) == 0xC0) {
if (tries_left != NULL)
*tries_left = apdu.sw2 & 0x0F;
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_PIN_CODE_INCORRECT);
if (data->flags & SC_PIN_CMD_NEED_PADDING)
pad = 1;
data->pin1.offset = 0;
if ((r = sc_build_pin(sbuf, sizeof(sbuf), &data->pin1, pad)) < 0)
return r;
len = r;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
ins = 0x20;
break;
case SC_PIN_CMD_CHANGE:
data->pin1.offset = len;
if ((r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
return r;
len += r;
ins = 0x24;
if (data->pin1.len == 0)
p1 = 1;
break;
case SC_PIN_CMD_UNBLOCK:
data->pin1.offset = len;
if ((r = sc_build_pin(sbuf+len, sizeof(sbuf)-len, &data->pin2, pad)) < 0)
return r;
len += r;
ins = 0x2C;
if (data->pin1.len == 0)
p1 |= 2;
if (data->pin2.len == 0)
p1 |= 1;
break;
default:
return SC_ERROR_NOT_SUPPORTED;
}
return sc_check_sw(card, apdu.sw1, apdu.sw2);
sc_format_apdu(card, apdu, SC_APDU_CASE_3_SHORT,
ins, p1, data->pin_reference);
apdu->lc = len;
apdu->datalen = len;
apdu->data = sbuf;
apdu->resplen = 0;
apdu->sensitive = 1;
return 0;
}
static int iso7816_reset_retry_counter(struct sc_card *card, unsigned int type, int ref,
const u8 *puk, size_t puklen, const u8 *_new,
size_t newlen)
static int iso7816_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
struct sc_apdu apdu;
u8 sbuf[SC_MAX_APDU_BUFFER_SIZE];
int r, p1 = 0, len = puklen + newlen;
struct sc_apdu local_apdu, *apdu;
int r;
if (len >= SC_MAX_APDU_BUFFER_SIZE)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
switch (type) {
case SC_AC_CHV:
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
if (tries_left)
*tries_left = -1;
/* 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
* special circumstances.
*/
if (data->apdu == NULL) {
r = iso7816_build_pin_apdu(card, &local_apdu, data);
if (r < 0)
return r;
data->apdu = &local_apdu;
}
if (puklen == 0) {
if (newlen == 0)
p1 = 3;
else
p1 = 2;
apdu = data->apdu;
if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) {
/* Transmit the APDU to the card */
r = sc_transmit_apdu(card, apdu);
/* Clear the buffer - it may contain pins */
memset((void *) apdu->data, 0, apdu->datalen);
} else {
if (newlen == 0)
p1 = 1;
else
p1 = 0;
/* Call the reader driver to collect
* the PIN and pass on the APDU to the card */
if (card->reader
&& card->reader->ops
&& card->reader->ops->enter_pin) {
r = card->reader->ops->enter_pin(card->reader,
card->slot,
data);
} else {
error(card->ctx,
"Card reader driver does not support "
"PIN entry through reader key pad");
r = SC_ERROR_NOT_SUPPORTED;
}
}
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0x2C, p1, ref);
memcpy(sbuf, puk, puklen);
memcpy(sbuf + puklen, _new, newlen);
apdu.lc = len;
apdu.datalen = len;
apdu.data = sbuf;
apdu.resplen = 0;
apdu.sensitive = 1;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, len);
/* Don't pass references to local variables up to the caller. */
if (data->apdu == &local_apdu)
data->apdu = NULL;
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
return sc_check_sw(card, apdu.sw1, apdu.sw2);
if (apdu->sw1 == 0x63) {
if ((apdu->sw2 & 0xF0) == 0xC0 && tries_left != NULL)
*tries_left = apdu->sw2 & 0x0F;
return SC_ERROR_PIN_CODE_INCORRECT;
}
return sc_check_sw(card, apdu->sw1, apdu->sw2);
}
static struct sc_card_operations iso_ops = {
@ -905,14 +912,12 @@ const struct sc_card_driver * sc_get_iso7816_driver(void)
iso_ops.get_challenge = iso7816_get_challenge;
iso_ops.create_file = iso7816_create_file;
iso_ops.delete_file = iso7816_delete_file;
iso_ops.verify = iso7816_verify;
iso_ops.set_security_env = iso7816_set_security_env;
iso_ops.restore_security_env = iso7816_restore_security_env;
iso_ops.compute_signature = iso7816_compute_signature;
iso_ops.decipher = iso7816_decipher;
iso_ops.reset_retry_counter = iso7816_reset_retry_counter;
iso_ops.change_reference_data = iso7816_change_reference_data;
iso_ops.check_sw = iso7816_check_sw;
iso_ops.pin_cmd = iso7816_pin_cmd;
}
return &iso_driver;
}

View File

@ -250,7 +250,11 @@ struct sc_reader_driver {
struct sc_reader_operations *ops;
};
/* slot flags */
#define SC_SLOT_CARD_PRESENT 0x00000001
/* slot capabilities */
#define SC_SLOT_CAP_DISPLAY 0x00000001
#define SC_SLOT_CAP_PIN_PAD 0x00000002
struct sc_slot_info {
int id;
@ -286,6 +290,47 @@ struct sc_reader {
int slot_count;
};
/* This will be the new interface for handling PIN commands.
* It is supposed to support pin pads (with or without display)
* attached to the reader.
*/
#define SC_PIN_CMD_VERIFY 0
#define SC_PIN_CMD_CHANGE 1
#define SC_PIN_CMD_UNBLOCK 2
#define SC_PIN_CMD_USE_PINPAD 0x0001
#define SC_PIN_CMD_NEED_PADDING 0x0002
#define SC_PIN_ENCODING_ASCII 0
#define SC_PIN_ENCODING_BCD 1
struct sc_pin_cmd_data {
unsigned int cmd;
unsigned int flags;
unsigned int pin_type; /* usually SC_AC_CHV */
int pin_reference;
struct sc_pin_cmd_pin {
const char *prompt; /* Prompt to display */
const u8 *data; /* PIN, if given by the appliction */
int len; /* set to -1 to get pin from pin pad */
size_t min_length; /* min/max length of PIN */
size_t max_length;
int encoding; /* ASCII-numeric, BCD, etc */
size_t pad_length; /* filled in by the card driver */
u8 pad_char;
size_t offset; /* offset relative to the APDU's
* argument buffer, where the
* PIN should go */
} pin1, pin2;
struct sc_apdu *apdu; /* APDU of the PIN command */
};
#define SC_DISCONNECT 0
#define SC_DISCONNECT_AND_RESET 1
#define SC_DISCONNECT_AND_UNPOWER 2
@ -310,13 +355,20 @@ struct sc_reader_operations {
int action);
int (*transmit)(struct sc_reader *reader, struct sc_slot_info *slot,
const u8 *sendbuf, size_t sendsize,
u8 *recvbuf, size_t *recvsize);
u8 *recvbuf, size_t *recvsize,
int control);
int (*lock)(struct sc_reader *reader, struct sc_slot_info *slot);
int (*unlock)(struct sc_reader *reader, struct sc_slot_info *slot);
int (*set_protocol)(struct sc_reader *reader, struct sc_slot_info *slot,
unsigned int proto);
int (*add_callback)(struct sc_reader *reader, struct sc_slot_info *slot,
const struct sc_event_listener *, void *arg);
/* Pin pad functions */
int (*display_message)(struct sc_reader *, struct sc_slot_info *,
const char *);
int (*enter_pin)(struct sc_reader *, struct sc_slot_info *,
struct sc_pin_cmd_data *);
};
@ -465,6 +517,12 @@ struct sc_card_operations {
int (*check_sw)(struct sc_card *card, int sw1, int sw2);
int (*card_ctl)(struct sc_card *card, unsigned long request,
void *data);
/* pin_cmd: verify/change/unblock command; optionally using the
* card's pin pad if supported.
*/
int (*pin_cmd)(struct sc_card *, struct sc_pin_cmd_data *,
int *tries_left);
};
struct sc_card_driver {
@ -642,6 +700,7 @@ int sc_compute_signature(struct sc_card *card, const u8 * data,
size_t data_len, u8 * out, size_t outlen);
int sc_verify(struct sc_card *card, unsigned int type, int ref, const u8 *buf,
size_t buflen, int *tries_left);
int sc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *, int *tries_left);
int sc_change_reference_data(struct sc_card *card, unsigned int type,
int ref, const u8 *old, size_t oldlen,
const u8 *newref, size_t newlen,
@ -649,6 +708,7 @@ int sc_change_reference_data(struct sc_card *card, unsigned int type,
int sc_reset_retry_counter(struct sc_card *card, unsigned int type,
int ref, const u8 *puk, size_t puklen,
const u8 *newref, size_t newlen);
int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad);
/* ISO 7816-9 */
int sc_create_file(struct sc_card *card, struct sc_file *file);

View File

@ -146,20 +146,34 @@ int sc_pkcs15_encode_aodf_entry(struct sc_context *ctx,
return r;
}
/*
* Verify a PIN.
*
* If the code given to us has zero length, this means we
* should ask the card reader to obtain the PIN from the
* reader's PIN pad
*/
int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card,
struct sc_pkcs15_pin_info *pin,
const u8 *pincode, size_t pinlen)
{
int r;
struct sc_card *card;
u8 pinbuf[SC_MAX_PIN_SIZE];
size_t len;
struct sc_pin_cmd_data args;
assert(p15card != NULL);
if (pin->magic != SC_PKCS15_PIN_MAGIC)
return SC_ERROR_OBJECT_NOT_VALID;
if (pinlen > pin->stored_length || pinlen < pin->min_length)
/* prevent buffer overflow from hostile card */
if (pin->stored_length > SC_MAX_PIN_SIZE)
return SC_ERROR_BUFFER_TOO_SMALL;
/* If application gave us a PIN, make sure it's within
* the valid range */
if (pinlen && (pinlen > pin->stored_length || pinlen < pin->min_length))
return SC_ERROR_INVALID_PIN_LENGTH;
card = p15card->card;
r = sc_lock(card);
SC_TEST_RET(card->ctx, r, "sc_lock() failed");
@ -168,14 +182,35 @@ int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card,
sc_unlock(card);
return r;
}
memset(pinbuf, pin->pad_char, pin->stored_length);
memcpy(pinbuf, pincode, pinlen);
len = pin->stored_length;
if (!(pin->flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING))
len = pinlen;
r = sc_verify(card, SC_AC_CHV, pin->reference,
pinbuf, len, &pin->tries_left);
memset(pinbuf, 0, pinlen);
/* Initialize arguments */
memset(&args, 0, sizeof(args));
args.cmd = SC_PIN_CMD_VERIFY;
args.pin_type = SC_AC_CHV;
args.pin_reference = pin->reference;
args.pin1.min_length = pin->min_length;
args.pin1.max_length = pin->stored_length;
args.pin1.pad_char = pin->pad_char;
if (pin->flags & SC_PKCS15_PIN_FLAG_NEEDS_PADDING)
args.flags |= SC_PIN_CMD_NEED_PADDING;
if (pinlen != 0) {
/* Good old-fashioned PIN verification */
args.pin1.data = pincode;
args.pin1.len = pinlen;
} else {
/* Use the reader's PIN PAD */
/* XXX need some sort of internationalization here */
args.flags |= SC_PIN_CMD_USE_PINPAD;
if (pin->flags & SC_PKCS15_PIN_FLAG_SO_PIN)
args.pin1.prompt = "Please enter SO PIN";
else
args.pin1.prompt = "Please enter PIN";
}
r = sc_pin_cmd(card, &args, &pin->tries_left);
sc_unlock(card);
if (r)
return r;
@ -183,6 +218,11 @@ int sc_pkcs15_verify_pin(struct sc_pkcs15_card *p15card,
return 0;
}
/*
* Change a PIN.
* FIXME: Edit this to use the new sc_pin_cmd call just as
* above.
*/
int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card,
struct sc_pkcs15_pin_info *pin,
const u8 *oldpin, size_t oldpinlen,

View File

@ -91,14 +91,15 @@ static int refresh_slot_attributes(struct sc_reader *reader,
static int ctapi_transmit(struct sc_reader *reader, struct sc_slot_info *slot,
const u8 *sendbuf, size_t sendsize,
u8 *recvbuf, size_t *recvsize)
u8 *recvbuf, size_t *recvsize,
int control)
{
struct ctapi_private_data *priv = GET_PRIV_DATA(reader);
u8 dad, sad;
unsigned short lr;
char rv;
dad = 0;
dad = control? 1 : 0;
sad = 2;
lr = *recvsize;
@ -375,6 +376,7 @@ const struct sc_reader_driver * sc_get_ctapi_driver()
ctapi_ops.release = ctapi_release;
ctapi_ops.connect = ctapi_connect;
ctapi_ops.disconnect = ctapi_disconnect;
ctapi_ops.enter_pin = ctbcs_pin_cmd;
return &ctapi_drv;
}

View File

@ -20,6 +20,8 @@
#include "internal.h"
#include "log.h"
#include "opensc.h"
#include "ctbcs.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
@ -112,7 +114,8 @@ static DWORD opensc_proto_to_pcsc(unsigned int proto)
static int pcsc_transmit(struct sc_reader *reader, struct sc_slot_info *slot,
const u8 *sendbuf, size_t sendsize,
u8 *recvbuf, size_t *recvsize)
u8 *recvbuf, size_t *recvsize,
int control)
{
SCARD_IO_REQUEST sSendPci, sRecvPci;
DWORD dwSendLength, dwRecvLength;
@ -151,8 +154,13 @@ static int pcsc_transmit(struct sc_reader *reader, struct sc_slot_info *slot,
if (dwRecvLength > 255)
dwRecvLength = 255;
rv = SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
&sRecvPci, recvbuf, &dwRecvLength);
if (!control) {
rv = SCardTransmit(card, &sSendPci, sendbuf, dwSendLength,
&sRecvPci, recvbuf, &dwRecvLength);
} else {
rv = SCardControl(card, sendbuf, dwSendLength,
recvbuf, &dwRecvLength);
}
if (rv != SCARD_S_SUCCESS) {
switch (rv) {
@ -422,6 +430,7 @@ const struct sc_reader_driver * sc_get_pcsc_driver()
pcsc_ops.release = pcsc_release;
pcsc_ops.connect = pcsc_connect;
pcsc_ops.disconnect = pcsc_disconnect;
pcsc_ops.enter_pin = ctbcs_pin_cmd;
return &pcsc_drv;
}

View File

@ -82,14 +82,16 @@ int sc_restore_security_env(struct sc_card *card, int se_num)
int sc_verify(struct sc_card *card, unsigned int type, int ref,
const u8 *pin, size_t pinlen, int *tries_left)
{
int r;
struct sc_pin_cmd_data data;
assert(card != NULL);
SC_FUNC_CALLED(card->ctx, 2);
if (card->ops->verify == NULL)
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
r = card->ops->verify(card, type, ref, pin, pinlen, tries_left);
SC_FUNC_RETURN(card->ctx, 2, r);
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_VERIFY;
data.pin_type = type;
data.pin_reference = ref;
data.pin1.data = pin;
data.pin1.len = pinlen;
return sc_pin_cmd(card, &data, tries_left);
}
int sc_change_reference_data(struct sc_card *card, unsigned int type,
@ -97,28 +99,148 @@ int sc_change_reference_data(struct sc_card *card, unsigned int type,
const u8 *newref, size_t newlen,
int *tries_left)
{
int r;
struct sc_pin_cmd_data data;
assert(card != NULL);
SC_FUNC_CALLED(card->ctx, 1);
if (card->ops->change_reference_data == NULL)
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
r = card->ops->change_reference_data(card, type, ref, old, oldlen,
newref, newlen, tries_left);
SC_FUNC_RETURN(card->ctx, 1, r);
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_CHANGE;
data.pin_type = type;
data.pin_reference = ref;
data.pin1.data = old;
data.pin1.len = oldlen;
data.pin2.data = newref;
data.pin2.len = newlen;
return sc_pin_cmd(card, &data, tries_left);
}
int sc_reset_retry_counter(struct sc_card *card, unsigned int type, int ref,
const u8 *puk, size_t puklen, const u8 *newref,
size_t newlen)
{
struct sc_pin_cmd_data data;
memset(&data, 0, sizeof(data));
data.cmd = SC_PIN_CMD_UNBLOCK;
data.pin_type = type;
data.pin_reference = ref;
data.pin1.data = puk;
data.pin1.len = puklen;
data.pin2.data = newref;
data.pin2.len = newlen;
return sc_pin_cmd(card, &data, NULL);
}
/*
* This is the new style pin command, which takes care of all PIN
* operations.
* If a PIN was given by the application, the card driver should
* send this PIN to the card. If no PIN was given, the driver should
* ask the reader to obtain the pin(s) via the pin pad
*/
int sc_pin_cmd(struct sc_card *card, struct sc_pin_cmd_data *data,
int *tries_left)
{
int r;
assert(card != NULL);
SC_FUNC_CALLED(card->ctx, 1);
if (card->ops->reset_retry_counter == NULL)
SC_FUNC_RETURN(card->ctx, 2, SC_ERROR_NOT_SUPPORTED);
r = card->ops->reset_retry_counter(card, type, ref, puk, puklen,
newref, newlen);
SC_FUNC_RETURN(card->ctx, 1, r);
SC_FUNC_CALLED(card->ctx, 2);
if (card->ops->pin_cmd) {
r = card->ops->pin_cmd(card, data, tries_left);
} else if (!(data->flags & SC_PIN_CMD_USE_PINPAD)) {
/* Card driver doesn't support new style pin_cmd, fall
* back to old interface */
r = SC_ERROR_NOT_SUPPORTED;
switch (data->cmd) {
case SC_PIN_CMD_VERIFY:
if (card->ops->verify != NULL)
r = card->ops->verify(card,
data->pin_type,
data->pin_reference,
data->pin1.data,
data->pin1.len,
tries_left);
break;
case SC_PIN_CMD_CHANGE:
if (card->ops->change_reference_data != NULL)
r = card->ops->change_reference_data(card,
data->pin_type,
data->pin_reference,
data->pin1.data,
data->pin1.len,
data->pin2.data,
data->pin2.len,
tries_left);
break;
case SC_PIN_CMD_UNBLOCK:
if (card->ops->reset_retry_counter != NULL)
r = card->ops->reset_retry_counter(card,
data->pin_type,
data->pin_reference,
data->pin1.data,
data->pin1.len,
data->pin2.data,
data->pin2.len);
break;
}
if (r == SC_ERROR_NOT_SUPPORTED)
error(card->ctx, "unsupported PIN operation (%d)",
data->cmd);
} else {
error(card->ctx, "Use of pin pad not supported by card driver");
r = SC_ERROR_NOT_SUPPORTED;
}
SC_FUNC_RETURN(card->ctx, 2, r);
}
/*
* This function will copy a PIN, convert and pad it as required
*/
int sc_build_pin(u8 *buf, size_t buflen, struct sc_pin_cmd_pin *pin, int pad)
{
size_t i = 0, j, pad_length = 0;
size_t pin_len = pin->len;
/* XXX: Should we silently truncate PINs that are too long? */
if (pin->max_length && pin_len > pin->max_length)
return SC_ERROR_INVALID_ARGUMENTS;
/* PIN given by application, encode if required */
if (pin->encoding == SC_PIN_ENCODING_ASCII) {
if (pin_len > buflen)
return SC_ERROR_BUFFER_TOO_SMALL;
memcpy(buf, pin->data, pin_len);
i = pin_len;
} else if (pin->encoding == SC_PIN_ENCODING_BCD) {
if (pin_len > 2 * buflen)
return SC_ERROR_BUFFER_TOO_SMALL;
for (i = j = 0; j < pin_len; j++) {
buf[i] <<= 4;
buf[i] |= pin->data[j] & 0xf;
if (j & 1)
i++;
}
if (j & 1) {
buf[i] <<= 4;
buf[i] |= pin->pad_char & 0xf;
i++;
}
}
/* Pad to maximum PIN length if requested */
if (pad) {
pad_length = pin->max_length;
if (pin->encoding == SC_PIN_ENCODING_BCD)
pad_length >>= 1;
}
if (pad_length > buflen)
return SC_ERROR_BUFFER_TOO_SMALL;
if (pad_length && i < pad_length) {
memset(buf + i, pin->pad_char, pad_length - i);
i = pad_length;
}
return i;
}

View File

@ -97,9 +97,10 @@ typedef struct sc_apdu {
u8 *resp; /* R-APDU data buffer */
size_t resplen; /* in: size of R-APDU buffer,
* out: length of data returned in R-APDU */
u8 sensitive; /* Set if the either the command or
u8 sensitive; /* Set if either the command or
* the response contains secrets,
* e.g. a PIN. */
u8 control; /* Set if APDU should go to the reader */
unsigned int sw1, sw2; /* Status words returned in R-APDU */
} sc_apdu_t;