- added partial support for GPK 4000

- made line parsing in opensc-explorer saner
- moved change_reference_data and reset_retry_counter to
  iso7816.c, where they belong
- added partial libreadline support to opensc-explorer


git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@206 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
jey 2002-02-10 18:04:03 +00:00
parent bfc15fa7fd
commit b4063302bf
9 changed files with 1074 additions and 111 deletions

View File

@ -10,10 +10,15 @@ lib_LTLIBRARIES = libopensc.la
libopensc_la_SOURCES = asn1.c base64.c sec.c log.c sc.c card.c iso7816.c \
pkcs15.c pkcs15-cert.c pkcs15-pin.c \
pkcs15-prkey.c pkcs15-sec.c pkcs15-cache.c \
card-setec.c card-flex.c \
card-setec.c card-flex.c card-gpk.c \
card-emv.c card-default.c
libopensc_la_LDFLAGS = -version-info 0:5:0
if HAVE_SSL
libopensc_la_LIBADD = @LIBPCSC@ @LIBCRYPTO@
else
libopensc_la_LIBADD = @LIBPCSC@
endif
include_HEADERS = opensc.h opensc-pkcs15.h opensc-emv.h
noinst_HEADERS = sc-asn1.h sc-log.h sc-internal.h

823
src/libopensc/card-gpk.c Normal file
View File

@ -0,0 +1,823 @@
/*
* GPK4000 driver for opensc
*
* Copyright (C) 2002 Olaf Kirch <okir@lst.de>
*/
#include "sc-internal.h"
#include "sc-log.h"
#include <stdlib.h>
#include <openssl/des.h>
#include <openssl/rand.h>
#ifdef HAVE_OPENSSL
/* GPK4000 variants */
enum {
GPK4000_su256,
GPK4000_s,
GPK4000_sp,
GPK4000_sdo,
};
#define GPK_SEL_MF 0x00
#define GPK_SEL_DF 0x01
#define GPK_SEL_EF 0x02
#define GPK_SEL_AID 0x04
#define GPK_FID_MF 0x3F00
/*
* GPK4000 private data
*/
struct gpk_private_data {
int variant;
/* access control bits of file most recently selected */
u_int16_t ac[3];
/* is non-zero if we should use secure messaging */
u_int8_t key_set : 1;
u_int8_t key_local : 1,
key_sfi : 5;
u_int8_t key[16];
};
#define OPSDATA(card) ((struct gpk_private_data *) ((card)->ops_data))
/*
* ATRs of GPK4000 cards courtesy of libscez
*/
static struct atrinfo {
unsigned char atr[SC_MAX_ATR_SIZE];
unsigned int atr_len;
int variant;
} atrlist[] = {
{ "\x3B\x27\x00\x80\x65\xA2\x04\x01\x01\x37", 10, GPK4000_s },
{ "\x3B\x27\x00\x80\x65\xA2\x05\x01\x01\x37", 10, GPK4000_sp },
{ "\x3B\x27\x00\x80\x65\xA2\x0C\x01\x01\x37", 10, GPK4000_su256 },
{ "\x3B\xA7\x00\x40\x14\x80\x65\xA2\x14\x01\x01\x37", 12, GPK4000_sdo },
{ "", 0, -1 }
};
/*
* Driver and card ops structures
*/
static struct sc_card_operations gpk_ops;
static const struct sc_card_driver gpk_drv = {
NULL,
"Gemplus GPK 4000 driver",
"gpk",
&gpk_ops
};
/*
* Identify the card variant based on the ATR
*/
static struct atrinfo *
gpk_identify(struct sc_card *card)
{
struct atrinfo *ai;
for (ai = atrlist; ai->atr_len; ai++) {
if (card->atr_len >= ai->atr_len
&& !memcmp(card->atr, ai->atr, ai->atr_len))
return ai;
}
return NULL;
}
/*
* return 1 iff this driver can handle the card
*/
static int
gpk_match(struct sc_card *card)
{
return gpk_identify(card)? 1 : 0;
}
/*
* Initialize the card struct
*/
static int
gpk_init(struct sc_card *card)
{
struct gpk_private_data *priv;
struct atrinfo *ai;
if (!(ai = gpk_identify(card)))
return SC_ERROR_INVALID_CARD;
card->ops_data = priv = malloc(sizeof(*priv));
if (card->ops_data == NULL)
return SC_ERROR_OUT_OF_MEMORY;
memset(priv, 0, sizeof(*priv));
priv->variant = ai->variant;
card->cla = 0;
return 0;
}
/*
* Card is being closed; discard any private data etc
*/
static int
gpk_finish(struct sc_card *card)
{
if (card->ops_data)
free(card->ops_data);
card->ops_data = NULL;
return 0;
}
/*
* Error code handling for the GPK4000.
* sc_sw_to_errorcode doesn't seem to handle all of the
* status words the GPK is capable of returning
*/
static int
gpk_sw_to_errorcode(struct sc_card *card, u_int8_t sw1, u_int8_t sw2)
{
u_int16_t sw = (sw1 << 8) | sw2;
if ((sw & 0xFFF0) == 0x63C0) {
error(card->ctx, "wrong PIN, %u tries left", sw&0xf);
return SC_ERROR_PIN_CODE_INCORRECT;
}
switch (sw) {
case 0x6400:
error(card->ctx, "wrong crypto context");
return SC_ERROR_OBJECT_NOT_VALID; /* XXX ??? */
case 0x6581:
error(card->ctx, "out of space on card or file");
return SC_ERROR_OUT_OF_MEMORY;
case 0x6981:
return SC_ERROR_FILE_NOT_FOUND;
case 0x6A80:
case 0x6b00:
return SC_ERROR_INVALID_ARGUMENTS;
}
return sc_sw_to_errorcode(card, sw1, sw2);
}
/*
* Select a DF/EF
*/
static int
match_path(struct sc_card *card, u_int16_t **pathptr, size_t *pathlen,
int need_info)
{
u_int16_t *curptr, *ptr;
size_t curlen, len;
size_t i;
curptr = (u_int16_t *) card->cache.current_path.value;
curlen = card->cache.current_path.len;
ptr = *pathptr;
len = *pathlen;
if (curlen < 1 || len < 1)
return 0;
/* Skip the MF if present */
if (ptr[0] != GPK_FID_MF) {
curptr++;
curlen--;
}
if (len < curlen)
return 0;
for (i = 0; i < len; i++) {
if (ptr[i] != curptr[i])
return 0;
}
/* Exact match? */
if (len == curlen && need_info)
return 0;
*pathptr = ptr + i;
*pathlen = len - i;
return 1;
}
static inline unsigned int
ac_to_acl(u_int16_t ac)
{
unsigned int npins, pin;
unsigned int res = 0;
npins = (ac >> 14) & 3;
if (npins == 3)
return SC_AC_NEVER;
pin = ac & 0xFF;
while (npins--) {
switch (pin & 7) {
case 0: res |= SC_AC_CHV1; break;
case 1: res |= SC_AC_CHV2; break;
default:return SC_AC_NEVER;
}
pin >>= 4;
}
/* Check whether secure messaging key is specified */
if (ac & 0x1F00)
res |= SC_AC_PRO;
return res;
}
/*
* Convert ACLs requested by the application to access condition
* bits supported by the GPK. Since these do not map 1:1 there's
* some fuzz involved.
*/
static inline void
acl_to_ac(unsigned int acl, u_int8_t *ac)
{
ac[0] = ac[1] = 0;
if (acl == SC_AC_NEVER) {
ac[0] = 0xC0;
return;
}
/* XXX should we set the "local" flag for PINs or not?
* OpenSC does not provide for a "lock file" operation
* that lets us freeze the ac bits after setting up the file.
*/
if (acl & SC_AC_CHV2) {
ac[0] += 0x40;
ac[1] |= 1;
}
if (acl & SC_AC_CHV1) {
ac[0] += 0x40;
ac[1] <<= 4;
ac[1] |= 0;
}
/* XXX should we set the "local" flag on key files or not?
* OpenSC does not provide for a "lock file" operation
* that lets us freeze the ac bits after setting up the file.
*/
if (acl & SC_AC_PRO) {
ac[0] |= 0x01;
}
}
static int
gpk_parse_fileinfo(struct sc_card *card,
const u_int8_t *buf, size_t buflen,
struct sc_file *file)
{
struct gpk_private_data *priv = OPSDATA(card);
const u_int8_t *sp, *end, *next;
int i;
memset(file, 0, sizeof(*file));
for (i = 0; i < SC_MAX_AC_OPS; i++)
file->acl[i] = SC_AC_UNKNOWN;
end = buf + buflen;
for (sp = buf; sp + 2 < end; sp = next) {
next = sp + 2 + sp[1];
if (next > end)
break;
if (sp[0] == 0x84) {
/* ignore if name is longer than what it should be */
if (sp[1] > sizeof(file->name))
continue;
memset(file->name, 0, sizeof(file->name));
memcpy(file->name, sp+2, sp[1]);
} else
if (sp[0] == 0x85) {
unsigned int ac1, ac2, ac3;
file->id = (sp[4] << 8) | sp[5];
file->size = (sp[8] << 8) | sp[9];
file->record_length = sp[7];
/* Map ACLs */
priv->ac[0] = (sp[10] << 8) | sp[11];
priv->ac[1] = (sp[12] << 8) | sp[13];
priv->ac[2] = (sp[14] << 8) | sp[15]; /* EF only */
ac1 = ac_to_acl(priv->ac[0]);
ac2 = ac_to_acl(priv->ac[1]);
ac3 = ac_to_acl(priv->ac[2]);
/* Examine file type */
switch (sp[6] & 7) {
case 0x01: case 0x02: case 0x03: case 0x04:
case 0x05: case 0x06: case 0x07:
file->type = SC_FILE_TYPE_WORKING_EF;
file->ef_structure = sp[6] & 7;
file->acl[SC_AC_OP_READ] = ac3;
file->acl[SC_AC_OP_WRITE] = ac3;
file->acl[SC_AC_OP_UPDATE] = ac1;
break;
case 0x00: /* 0x38 is DF */
file->type = SC_FILE_TYPE_DF;
file->acl[SC_AC_OP_SELECT] = SC_AC_NONE;
file->acl[SC_AC_OP_LOCK] = ac1;
/* Icky: the GPK uses different ACLs
* for creating data files and
* 'sensitive' i.e. key files */
file->acl[SC_AC_OP_CREATE] = ac2;
file->acl[SC_AC_OP_DELETE] = SC_AC_NEVER;
file->acl[SC_AC_OP_REHABILITATE] = SC_AC_NEVER;
file->acl[SC_AC_OP_INVALIDATE] = SC_AC_NEVER;
file->acl[SC_AC_OP_LIST_FILES] = SC_AC_NEVER;
break;
}
}
}
if (file->record_length)
file->record_count = file->size / file->record_length;
file->magic = SC_FILE_MAGIC;
return 0;
}
static int
gpk_select(struct sc_card *card, u_int8_t kind,
const u_int8_t *buf, size_t buflen,
struct sc_file *file)
{
struct gpk_private_data *priv = OPSDATA(card);
struct sc_apdu apdu;
u_int8_t resbuf[SC_MAX_APDU_BUFFER_SIZE];
int r;
/* If we're about to select a DF, invalidate secure messaging keys */
if (kind == GPK_SEL_MF || kind == GPK_SEL_DF) {
memset(priv->key, 0, sizeof(priv->key));
priv->key_set = 0;
}
/* do the apdu thing */
sc_format_apdu(card, &apdu, SC_APDU_CASE_3_SHORT, 0xA4, kind, 0);
apdu.data = buf;
apdu.datalen = buflen;
apdu.lc = apdu.datalen;
apdu.resp = resbuf;
apdu.resplen = file? sizeof(resbuf) : 0;
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
/* Nothing we can say about it... invalidate
* path cache */
if (kind == GPK_SEL_AID) {
card->cache.current_path.len = 0;
}
if (file == NULL)
return 0;
return gpk_parse_fileinfo(card, apdu.resp, apdu.resplen, file);
}
static int
gpk_select_id(struct sc_card *card, u_int8_t kind, u_int16_t fid,
struct sc_file *file)
{
struct sc_path *cp = &card->cache.current_path;
u_int8_t fbuf[2];
int r;
fbuf[0] = fid >> 8;
fbuf[1] = fid & 0xff;
r = gpk_select(card, kind, fbuf, 2, file);
/* Fix up the path cache */
if (r == 0) {
u_int16_t *path = (u_int16_t *) cp->value;
if (fid == GPK_FID_MF) {
path[0] = fid;
cp->len = 1;
} else
if (cp->len + 1 <= SC_MAX_PATH_SIZE / 2) {
path[cp->len++] = fid;
} else {
cp->len = 0;
}
} else {
cp->len = 0;
}
return r;
}
static int
gpk_select_file(struct sc_card *card, const struct sc_path *path,
struct sc_file *file)
{
u_int16_t pathtmp[SC_MAX_PATH_SIZE/2];
u_int16_t *pathptr;
size_t pathlen, n;
int locked = 0, r = 0, use_relative = 0;
u_int8_t leaf_type;
SC_FUNC_CALLED(card->ctx, 3);
/* Handle the AID case first */
if (path->type == SC_PATH_TYPE_DF_NAME) {
if (path->len > 16)
return SC_ERROR_INVALID_ARGUMENTS;
r = gpk_select(card, GPK_SEL_AID,
path->value, path->len, file);
goto done;
}
/* Now we know we're dealing with 16bit FIDs, either as
* an absolute path name (SC_PATH_TYPE_PATH) or a relative
* FID (SC_PATH_TYPE_FILE_ID)
*
* The API should really tell us whether this is a DF or EF
* we're selecting. All we can do is read tea leaves...
*/
leaf_type = GPK_SEL_EF;
try_again:
if ((path->len & 1) || path->len > sizeof(pathtmp))
return SC_ERROR_INVALID_ARGUMENTS;
pathptr = pathtmp;
for (n = 0; n < path->len; n += 2)
pathptr[n>>1] = (path->value[n] << 8)|path->value[n+1];
pathlen = path->len >> 1;
/* See whether we can skip an initial portion of the
* (absolute) path */
if (path->type == SC_PATH_TYPE_PATH) {
use_relative = match_path(card, &pathptr, &pathlen, file != 0);
if (pathlen == 0)
goto done;
} else {
/* SC_PATH_TYPE_FILEID */
if (pathlen > 1)
return SC_ERROR_INVALID_ARGUMENTS;
use_relative = 1;
}
if (pathlen == 1 && pathptr[0] == GPK_FID_MF) {
/* Select just the MF */
leaf_type = GPK_SEL_MF;
} else {
if (!locked++) {
r = sc_lock(card);
SC_TEST_RET(card->ctx, r, "sc_lock() failed");
}
/* Do we need to select the MF first? */
if (!use_relative) {
r = gpk_select_id(card, GPK_SEL_MF, GPK_FID_MF, NULL);
if (r)
sc_unlock(card);
SC_TEST_RET(card->ctx, r, "Unable to select MF");
/* Consume the MF FID if it's there */
if (pathptr[0] == GPK_FID_MF) {
pathptr++;
pathlen--;
}
if (pathlen == 0)
goto done;
}
/* Next comes a DF, if at all.
* This loop can deal with nesting levels > 1 even
* though the GPK4000 doesn't support it. */
while (pathlen > 1) {
r = gpk_select_id(card, GPK_SEL_DF, pathptr[0], NULL);
if (r)
sc_unlock(card);
SC_TEST_RET(card->ctx, r, "Unable to select DF");
pathptr++;
pathlen--;
}
}
/* Remaining component will be a DF or EF. How do we find out?
* All we can do is try */
r = gpk_select_id(card, leaf_type, pathptr[0], file);
if (r) {
/* Did we guess EF, and were wrong? If so, invalidate
* path cache and try again; this time aiming for a DF */
if (leaf_type == GPK_SEL_EF) {
card->cache.current_path.len = 0;
leaf_type = GPK_SEL_DF;
goto try_again;
}
}
done:
if (locked)
sc_unlock(card);
return r;
}
/*
* Secure messaging
*/
static int
gpk_compute_crycks(struct sc_card *card, struct sc_apdu *apdu,
u_int8_t *crycks1)
{
struct gpk_private_data *priv = OPSDATA(card);
des_key_schedule k1, k2;
u_int8_t in[8], out[8], block[64];
unsigned int len = 0, i, j;
/* Set the key schedule */
des_set_key_unchecked((des_cblock *) priv->key, k1);
des_set_key_unchecked((des_cblock *) (priv->key+8), k2);
/* Fill block with 0x00 and then with the data. */
memset(block, 0x00, sizeof(block));
block[len++] = apdu->cla;
block[len++] = apdu->ins;
block[len++] = apdu->p1;
block[len++] = apdu->p2;
block[len++] = apdu->lc + 3;
if ((i = apdu->datalen) + len > sizeof(block))
i = sizeof(block) - len;
memcpy(block+len, apdu->data, i);
len += i;
/* Set IV */
memset(in, 0x00, 8);
for (j = 0; j < len; ) {
for (i = 0; i < 8; i++, j++)
in[i] ^= block[j];
des_ecb3_encrypt((des_cblock *)in,
(des_cblock *)out,
k1, k2, k1, DES_ENCRYPT);
memcpy(in, out, 8);
}
memcpy((u_int8_t *) (apdu->data + apdu->datalen), out + 5, 3);
apdu->datalen += 3;
apdu->lc += 3;
apdu->le = 3;
if (crycks1)
memcpy(crycks1, out, 3);
memset(k1, 0, sizeof(k1));
memset(k2, 0, sizeof(k2));
memset(in, 0, sizeof(in));
memset(out, 0, sizeof(out));
memset(block, 0, sizeof(block));
return 0;
}
/*
* Create a file or directory.
* This is a bit tricky because we abuse the ef_structure
* field to transport file types that are non-standard
* (the GPK4000 has lots of bizarre file types).
*/
static int
gpk_create_file(struct sc_card *card, struct sc_file *file)
{
struct gpk_private_data *priv = OPSDATA(card);
struct sc_apdu apdu;
u_int8_t data[28+3], crycks[3], resp[3];
size_t datalen, namelen;
int r;
/* Prepare APDU */
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x80; /* assume no secure messaging */
apdu.cse = SC_APDU_CASE_3_SHORT;
apdu.ins = 0xE0;
apdu.p2 = 0x00;
/* clear data */
memset(data, 0, sizeof(data));
datalen = 12;
/* FID */
data[0] = file->id >> 8;
data[1] = file->id & 0xFF;
/* encode ACLs */
if (file->type == SC_FILE_TYPE_DF) {
/* The GPK4000 has separate AC bits for
* creating sensitive files and creating
* data files. Since OpenSC has just the notion
* of "file" we use the same ACL for both AC words
*/
apdu.p1 = 0x01; /* create DF */
data[2] = 0x38;
acl_to_ac(file->acl[SC_AC_OP_CREATE], data + 6);
acl_to_ac(file->acl[SC_AC_OP_CREATE], data + 8);
if ((namelen = file->namelen) != 0) {
if (namelen > 16)
return SC_ERROR_INVALID_ARGUMENTS;
memcpy(data+datalen, file->name, namelen);
data[5] = namelen;
datalen += namelen;
}
} else {
apdu.p1 = 0x02; /* create EF */
data[2] = file->ef_structure;
data[3] = file->record_length;
data[4] = file->size >> 8;
data[5] = file->size & 0xff;
acl_to_ac(file->acl[SC_AC_OP_UPDATE], data + 6);
acl_to_ac(file->acl[SC_AC_OP_WRITE], data + 8);
acl_to_ac(file->acl[SC_AC_OP_READ], data + 10);
}
apdu.data = data;
apdu.datalen = datalen;
apdu.lc = datalen;
if (priv->key_set) {
apdu.cla = 0x84;
apdu.cse = SC_APDU_CASE_4_SHORT;
r = gpk_compute_crycks(card, &apdu, crycks);
if (r)
return r;
apdu.resp = resp;
apdu.resplen = sizeof(resp); /* XXX? */
}
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
if (priv->key_set) {
/* verify CRYCKS response? */
if (apdu.resplen != 3
|| memcmp(resp, crycks, 3)) {
printf("XXX Secure messaging: bad resp\n");
}
}
return r;
}
/*
* Set the secure messaging key following a Select FileKey
*/
static int
gpk_set_filekey(const u_int8_t *key, const u_int8_t *challenge,
const u_int8_t *r_rn, u_int8_t *kats)
{
des_key_schedule k1, k2;
des_cblock out;
int r = 0;
des_set_key_unchecked((des_cblock *) key, k1);
des_set_key_unchecked((des_cblock *) (key+8), k2);
des_ecb3_encrypt((des_cblock *)(r_rn+4), (des_cblock *) kats,
k1, k2, k1, DES_ENCRYPT);
des_ecb3_encrypt((des_cblock *)(r_rn+4), (des_cblock *) (kats+8),
k2, k1, k2, DES_ENCRYPT);
/* Verify Cryptogram presented by the card terminal
* XXX: what is the appropriate error code to return
* here? INVALID_ARGS doesn't seem quite right
*/
des_set_key_unchecked((des_cblock *) kats, k1);
des_set_key_unchecked((des_cblock *) (kats+8), k2);
des_ecb3_encrypt((des_cblock *) challenge, &out,
k1, k2, k1, DES_ENCRYPT );
if (memcmp(r_rn, out+4, 4) != 0)
r = SC_ERROR_INVALID_ARGUMENTS;
memset(k1, 0, sizeof(k1));
memset(k2, 0, sizeof(k2));
memset(out, 0, sizeof(out));
return r;
}
/*
* Verify a key presented by the user for secure messaging
*/
static int
gpk_select_key(struct sc_card *card, int ref, const u8 *buf, size_t buflen)
{
struct gpk_private_data *priv = OPSDATA(card);
struct sc_apdu apdu;
u_int8_t random[8], resp[258];
unsigned int n, sfi, key_sfi = 0;
int r;
if (buflen != 16)
return SC_ERROR_INVALID_ARGUMENTS;
/* The opensc API doesn't tell us what key it wants to
* select, and why. We need to look at the ACs of
* the most recently selected file and guess
*/
key_sfi = 0;
for (n = 0; n < 3; n++) {
sfi = (priv->ac[n] >> 8) & 0x3F;
if (sfi & 0xF) {
if (key_sfi && key_sfi != sfi) {
/* Hm, the file has ACLs with two
* different keys. I'm unable to guess
* which one I should use, so I throw
* up my hands in disgust.
*/
/* XXX fix errror code? */
return SC_ERROR_INVALID_ARGUMENTS;
}
key_sfi = sfi;
}
}
/* If no key required, assume transport key :-/ */
if (key_sfi == 0)
key_sfi = 0x01;
/* XXX now do the SelFk */
RAND_pseudo_bytes(random, sizeof(random));
memset(&apdu, 0, sizeof(apdu));
apdu.cla = 0x80;
apdu.cse = SC_APDU_CASE_4_SHORT;
apdu.ins = 0x28;
apdu.p1 = ref << 1;
apdu.p2 = key_sfi;
apdu.data = random;
apdu.datalen = sizeof(random);
apdu.lc = apdu.datalen;
apdu.resp = resp;
apdu.resplen = sizeof(resp);
r = sc_transmit_apdu(card, &apdu);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
r = gpk_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
SC_TEST_RET(card->ctx, r, "Card returned error");
if (apdu.resplen != 12) {
r = SC_ERROR_UNKNOWN_REPLY;
} else
if ((r = gpk_set_filekey(buf, random, resp, priv->key)) == 0) {
priv->key_set = 1;
priv->key_local = (key_sfi & 0x20)? 1 : 0;
priv->key_sfi = key_sfi & 0x1f;
}
memset(resp, 0, sizeof(resp));
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);
}
return SC_ERROR_INVALID_ARGUMENTS;
}
/*
* Initialize the driver struct
*/
static const struct sc_card_driver *
sc_get_driver()
{
if (gpk_ops.match_card == NULL) {
const struct sc_card_driver *iso_drv;
iso_drv = sc_get_iso7816_driver();
gpk_ops = *iso_drv->ops;
gpk_ops.match_card = gpk_match;
gpk_ops.init = gpk_init;
gpk_ops.finish = gpk_finish;
gpk_ops.select_file = gpk_select_file;
/* The GPK4000 doesn't have a read directory command. */
//gpk_ops.list_files = gpk_list_files;
gpk_ops.verify = gpk_verify;
gpk_ops.create_file = gpk_create_file;
}
return &gpk_drv;
}
const struct sc_card_driver *
sc_get_gpk_driver()
{
return sc_get_driver();
}
#endif /* ifdef OPENSSL */

View File

@ -57,7 +57,7 @@ static int iso7816_read_record(struct sc_card *card,
int r;
sc_format_apdu(card, &apdu, SC_APDU_CASE_2_SHORT, 0xB2, rec_nr, 0);
apdu.p2 = (rec_nr & SC_READ_RECORD_EF_ID_MASK) << 3;
apdu.p2 = (flags & SC_READ_RECORD_EF_ID_MASK) << 3;
if (flags & SC_READ_RECORD_BY_REC_NR)
apdu.p2 |= 0x04;
@ -663,6 +663,86 @@ static int iso7816_compute_signature(struct sc_card *card,
SC_FUNC_RETURN(card->ctx, 4, sc_sw_to_errorcode(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)
{
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) {
case SC_AC_CHV1:
case SC_AC_CHV2:
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;
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);
}
return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
}
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)
{
struct sc_apdu apdu;
u8 sbuf[MAX_BUFFER_SIZE];
int r, p1 = 0, len = puklen + newlen;
if (len >= MAX_BUFFER_SIZE)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
switch (type) {
case SC_AC_CHV1:
case SC_AC_CHV2:
break;
default:
return SC_ERROR_INVALID_ARGUMENTS;
}
if (puklen == 0) {
if (newlen == 0)
p1 = 3;
else
p1 = 2;
} else {
if (newlen == 0)
p1 = 1;
else
p1 = 0;
}
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;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, len);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
return sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2);
}
static struct sc_card_operations iso_ops = {
NULL,
@ -698,6 +778,8 @@ const struct sc_card_driver * sc_get_iso7816_driver(void)
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.reset_retry_counter = iso7816_reset_retry_counter;
iso_ops.change_reference_data = iso7816_change_reference_data;
}
return &iso_driver;
}

View File

@ -347,11 +347,13 @@ struct sc_card_operations {
* to the function decipher. */
int (*compute_signature)(struct sc_card *card, const u8 * data,
size_t data_len, u8 * out, size_t outlen);
int (*change_reference_data)(struct sc_card *card, int ref_qualifier,
int (*change_reference_data)(struct sc_card *card, unsigned int type,
int ref_qualifier,
const u8 *old, size_t oldlen,
const u8 *newref, size_t newlen,
int *tries_left);
int (*reset_retry_counter)(struct sc_card *card, int ref_qualifier,
int (*reset_retry_counter)(struct sc_card *card, unsigned int type,
int ref_qualifier,
const u8 *puk, size_t puklen,
const u8 *newref, size_t newlen);
/*
@ -517,11 +519,13 @@ 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_change_reference_data(struct sc_card *card, int ref, const u8 *old,
size_t oldlen, const u8 *newref, size_t newlen,
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,
int *tries_left);
int sc_reset_retry_counter(struct sc_card *card, int ref, const u8 *puk,
size_t puklen, const u8 *newref, size_t newlen);
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);
/* ISO 7816-9 */
int sc_create_file(struct sc_card *card, struct sc_file *file);
@ -545,6 +549,7 @@ extern const struct sc_card_driver *sc_get_iso7816_driver(void);
extern const struct sc_card_driver *sc_get_emv_driver(void);
extern const struct sc_card_driver *sc_get_setec_driver(void);
extern const struct sc_card_driver *sc_get_flex_driver(void);
extern const struct sc_card_driver *sc_get_gpk_driver(void);
extern const struct sc_card_driver *sc_get_default_driver(void);
#ifdef __cplusplus

View File

@ -288,7 +288,7 @@ int sc_pkcs15_change_pin(struct sc_pkcs15_card *p15card,
memset(pinbuf, pin->pad_char, pin->stored_length * 2);
memcpy(pinbuf, oldpin, oldpinlen);
memcpy(pinbuf + pin->stored_length, newpin, newpinlen);
r = sc_change_reference_data(card, pin->auth_id.value[0], pinbuf,
r = sc_change_reference_data(card, SC_AC_CHV1, pin->auth_id.value[0], pinbuf,
pin->stored_length, pinbuf+pin->stored_length,
pin->stored_length, &pin->tries_left);
memset(pinbuf, 0, pin->stored_length * 2);

View File

@ -182,12 +182,12 @@ int sc_establish_context(struct sc_context **ctx_out)
#if 1
ctx->card_drivers[i++] = sc_get_flex_driver();
#endif
#if 1
ctx->card_drivers[i++] = sc_get_iso7816_driver();
#endif
#if 1
ctx->card_drivers[i++] = sc_get_emv_driver();
#endif
#if 1 && defined(HAVE_OPENSSL)
ctx->card_drivers[i++] = sc_get_gpk_driver();
#endif
#if 1
/* this should be last in line */
ctx->card_drivers[i++] = sc_get_default_driver();

View File

@ -113,68 +113,33 @@ int sc_verify(struct sc_card *card, unsigned int type, int ref,
SC_FUNC_RETURN(card->ctx, 2, r);
}
int sc_change_reference_data(struct sc_card *card, int ref, const u8 *old,
size_t oldlen, const u8 *new, size_t newlen,
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,
int *tries_left)
{
struct sc_apdu apdu;
u8 sbuf[MAX_BUFFER_SIZE];
int r, p1 = 0, len = oldlen + newlen;
int r;
assert(card != NULL);
SC_FUNC_CALLED(card->ctx, 1);
if (len >= MAX_BUFFER_SIZE)
SC_FUNC_RETURN(card->ctx, 1, 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;
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);
}
SC_FUNC_RETURN(card->ctx, 1, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2));
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);
}
int sc_reset_retry_counter(struct sc_card *card, int ref, const u8 *puk,
size_t puklen, const u8 *new, size_t newlen)
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_apdu apdu;
u8 sbuf[MAX_BUFFER_SIZE];
int r, p1 = 0, len = puklen + newlen;
int r;
if (len >= MAX_BUFFER_SIZE)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INVALID_ARGUMENTS);
if (puklen == 0) {
if (newlen == 0)
p1 = 3;
else
p1 = 2;
} else {
if (newlen == 0)
p1 = 1;
else
p1 = 0;
}
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;
r = sc_transmit_apdu(card, &apdu);
memset(sbuf, 0, len);
SC_TEST_RET(card->ctx, r, "APDU transmit failed");
SC_FUNC_RETURN(card->ctx, 1, sc_sw_to_errorcode(card, apdu.sw1, apdu.sw2));
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);
}

View File

@ -13,6 +13,7 @@ endif
opensc_tool_SOURCES = opensc-tool.c util.c
opensc_tool_LDADD = @GETOPTSRC@
opensc_explorer_SOURCES = opensc-explorer.c util.c
## FIXME: conditionally add -lreadline
opensc_explorer_LDADD = @GETOPTSRC@
pkcs15_tool_SOURCES = pkcs15-tool.c util.c
pkcs15_tool_LDADD = @GETOPTSRC@

View File

@ -22,6 +22,13 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#undef USE_READLINE
#ifdef USE_READLINE
#include <readline/readline.h>
#endif
#include "util.h"
int opt_reader = 0;
@ -171,6 +178,7 @@ int do_ls()
check_ret(r, SC_AC_OP_SELECT, "unable to select file", &current_file);
return -1;
}
file.id = (cur[0] << 8) | cur[1];
cur += 2;
count -= 2;
print_file(&file);
@ -229,12 +237,56 @@ int do_cd(const char *arg)
return 0;
}
int do_cat(const char *arg)
int read_and_print_binary_file(struct sc_file *file)
{
unsigned int idx = 0;
u8 buf[128];
size_t count;
int r;
count = file->size;
while (count) {
int c = count > sizeof(buf) ? sizeof(buf) : count;
r = sc_read_binary(card, idx, buf, c, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
return -1;
}
if (r != c) {
printf("expecting %d, got only %d bytes.\n", c, r);
return -1;
}
hex_dump_asc(stdout, buf, c, idx);
idx += c;
count -= c;
}
return 0;
}
int read_and_print_record_file(struct sc_file *file)
{
u8 buf[256];
int rec, r;
for (rec = 0; ; rec++) {
r = sc_read_record(card, rec, buf, sizeof(buf), SC_READ_RECORD_BY_REC_NR);
if (r == SC_ERROR_RECORD_NOT_FOUND)
return 0;
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", file);
return -1;
}
printf("Record %d:\n", rec);
hex_dump_asc(stdout, buf, r, 0);
}
return 0;
}
int do_cat(const char *arg)
{
int r, error = 0;
size_t count = 0;
unsigned int idx = 0;
struct sc_path path;
struct sc_file file;
int not_current = 1;
@ -254,26 +306,10 @@ int do_cat(const char *arg)
return -1;
}
}
count = file.size;
while (count) {
int c = count > sizeof(buf) ? sizeof(buf) : count;
r = sc_read_binary(card, idx, buf, c, 0);
if (r < 0) {
check_ret(r, SC_AC_OP_READ, "read failed", &file);
error = 1;
goto err;
}
if (r != c) {
printf("expecting %d, got only %d bytes.\n", c, r);
error = 1;
goto err;
}
hex_dump_asc(stdout, buf, c, idx);
idx += c;
count -= c;
}
err:
if (file.ef_structure == SC_FILE_EF_TRANSPARENT)
read_and_print_binary_file(&file);
else
read_and_print_record_file(&file);
if (not_current) {
r = sc_select_file(card, &current_path, NULL);
if (r) {
@ -478,7 +514,7 @@ usage:
int do_verify(const char *arg, const char *arg2)
{
const char *types[] = {
"CHV", "KEY"
"CHV", "KEY", "PRO"
};
int i, type = -1, ref, r, tries_left = -1;
u8 buf[30];
@ -486,7 +522,7 @@ int do_verify(const char *arg, const char *arg2)
if (strlen(arg) == 0 || strlen(arg2) == 0)
goto usage;
for (i = 0; i < 2; i++)
for (i = 0; i < 3; i++)
if (strncasecmp(arg, types[i], 3) == 0) {
type = i;
break;
@ -499,6 +535,11 @@ int do_verify(const char *arg, const char *arg2)
printf("Invalid key reference.\n");
goto usage;
}
if (arg2[0] == '"') {
for (++arg2, i = 0; i < sizeof(buf) && arg2[i] != '"'; i++)
buf[i] = arg2[i];
buflen = i;
} else
if (sc_hex_to_bin(arg2, buf, &buflen) != 0) {
printf("Invalid key value.\n");
goto usage;
@ -510,6 +551,9 @@ int do_verify(const char *arg, const char *arg2)
case 1:
type = SC_AC_AUT;
break;
case 2:
type = SC_AC_PRO;
break;
}
r = sc_verify(card, type, ref, buf, buflen, &tries_left);
if (r) {
@ -721,10 +765,52 @@ void usage()
printf(" %s\n", cmds[i]);
}
static int parse_line(char *in, char **argv)
{
int argc;
for (argc = 0; argc < 3; argc++) {
in += strspn(in, " \t\n");
if (*in == '\0')
return argc;
if (*in == '"') {
/* Parse quoted string */
argv[argc] = in++;
in += strcspn(in, "\"");
if (*in++ != '"')
return 0;
} else {
/* White space delimited word */
argv[argc] = in;
in += strcspn(in, " \t\n");
}
if (*in != '\0')
*in++ = '\0';
}
return argc;
}
#ifndef USE_READLINE
char * readline(const char *prompt)
{
static char buf[128];
printf("%s", prompt);
fflush(stdout);
if (fgets(buf, sizeof(buf), stdin) == NULL)
return NULL;
if (strlen(buf) == 0)
return NULL;
if (buf[strlen(buf)-1] == '\n')
buf[strlen(buf)-1] = '\0';
return buf;
}
#endif
int main(int argc, char * const argv[])
{
int r, c, long_optind = 0, err = 0;
char line[80], cmd[80], arg[80], arg2[80];
char *line, *cargv[3];
printf("OpenSC Explorer version %s\n", sc_version);
@ -789,33 +875,29 @@ int main(int argc, char * const argv[])
}
while (1) {
int i;
char prompt[40];
printf("OpenSC [");
sprintf(prompt, "OpenSC [");
for (i = 0; i < current_path.len; i++) {
if ((i & 1) == 0 && i)
printf("/");
printf("%02X", current_path.value[i]);
sprintf(prompt+strlen(prompt), "/");
sprintf(prompt+strlen(prompt), "%02X", current_path.value[i]);
}
printf("]> ");
fflush(stdout);
fflush(stdin);
if (fgets(line, sizeof(line), stdin) == NULL)
break;
if (strlen(line) == 0)
break;
r = sscanf(line, "%s %s %s", cmd, arg, arg2);
sprintf(prompt+strlen(prompt), "]> ");
line = readline(prompt);
if (line == NULL)
break;
r = parse_line(line, cargv);
if (r < 1)
continue;
if (r < 3)
arg2[0] = 0;
if (r < 2)
arg[0] = 0;
r = ambiguous_match(cmds, nr_cmds, cmd);
while (r < 3)
cargv[r++] = "";
r = ambiguous_match(cmds, nr_cmds, cargv[0]);
if (r < 0) {
usage();
continue;
}
handle_cmd(r, arg, arg2);
handle_cmd(r, cargv[1], cargv[2]);
}
end:
die(err);