- 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:
parent
604e9cd318
commit
9acca0e724
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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_ */
|
||||
|
||||
|
|
|
@ -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[] = {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue