Updated piv driver by Douglas E. Engert:

the PIV driver no longer need to set the card max_*_size parameters
   to get around emulating read_binary and write_binary. It can
   now handle partial reads and writes.

   The assumptions for write_binary are that the first chuck will
   have idx = 0, and the last chunk will write the last byte.
   The flags parameter will contain the total length.

   The only write_binary operations are done when initializing
   a card, and this is only done from piv-tool.c which was modified
   to pass in the length and other flags.

   Piv-tool continues to be a primative test tool for inializing test
   cards. But it has been expanded to be able to write other objects
   on test cards.

   The serial number of a PIV  card is obtained from the CHUID object
   if present which has a FASC-N which is an ID number created by the
   issuer. Normally PIV cards are issued the U.S. Federal government
   But there are ways to use the same cards with a non government CA.
   This is then be referred to as PIV Compatible. In this case,
   the FASC-N should start with an agency code = 9999 and an RFC 4122
   GUID should be present in the CHUID. If this is the case, the GUID
   is used as the serial number.

   Windows 7 comes with a PIV card card driver, but to get it use one of
   these card the CHUID is required. (piv-tool can now write one.



git-svn-id: https://www.opensc-project.org/svnp/opensc/trunk@3998 c6295689-39f2-0310-b995-f0e70906c6a9
This commit is contained in:
aj 2010-02-05 06:16:37 +00:00
parent 119c7751c7
commit 9406ce2885
2 changed files with 204 additions and 52 deletions

View File

@ -3,7 +3,7 @@
* card-default.c: Support for cards with no driver
*
* Copyright (C) 2001, 2002 Juha Yrjölä <juha.yrjola@iki.fi>
* Copyright (C) 2005,2006,2007 Douglas E. Engert <deengert@anl.gov>
* Copyright (C) 2005,2006,2007,2008,2009,2010 Douglas E. Engert <deengert@anl.gov>
* Copyright (C) 2006, Identity Alliance, Thomas Harning <thomas.harning@identityalliance.com>
* Copyright (C) 2007, EMC, Russell Larner <rlarner@rsa.com>
*
@ -77,11 +77,11 @@ typedef struct piv_private_data {
int enumtag;
int selected_obj; /* The index into the piv_objects last selected */
int return_only_cert; /* return the cert from the object */
int rb_state; /* first time -1, 0, in middle, 1 at eof */
size_t max_recv_size; /* saved size, need to lie to pkcs15_read_file */
size_t max_send_size;
int rwb_state; /* first time -1, 0, in middle, 1 at eof */
int key_ref; /* saved from set_security_env and */
int alg_id; /* used in decrypt, signature */
u8* w_buf; /* write_binary buffer */
size_t w_buf_len; /* length of w_buff */
piv_obj_cache_t obj_cache[PIV_OBJ_LAST_ENUM];
} piv_private_data_t;
@ -246,7 +246,7 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2,
SC_FUNC_CALLED(card->ctx,1);
sc_debug(card->ctx, "%02x %02x %02x %d : %d %d\n",
ins, p1, p2, sendbuflen , priv->max_send_size, priv->max_recv_size);
ins, p1, p2, sendbuflen , card->max_send_size, card->max_recv_size);
rbuf = rbufinitbuf;
rbuflen = sizeof(rbufinitbuf);
@ -272,7 +272,7 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2,
if (recvbuf) {
apdu.resp = rbuf;
apdu.le = (priv->max_recv_size <= rbuflen)? priv->max_recv_size : rbuflen;
apdu.le = (card->max_recv_size <= rbuflen)? card->max_recv_size : rbuflen;
apdu.resplen = rbuflen;
} else {
apdu.resp = rbuf;
@ -280,16 +280,11 @@ static int piv_general_io(sc_card_t *card, int ins, int p1, int p2,
apdu.resplen = 0;
}
/* TODO if read_binary is fixed, this is not needed */
card->max_recv_size = priv->max_recv_size;
sc_debug(card->ctx,"calling sc_transmit_apdu flags=%x le=%d, resplen=%d, resp=%p",
apdu.flags, apdu.le, apdu.resplen, apdu.resp);
/* with new adpu.c and chaining, this actually reads the whole object */
r = sc_transmit_apdu(card, &apdu);
/* TODO if read_binary is fixed, this is not needed */
card->max_recv_size = 0xffff;
sc_debug(card->ctx,"DEE r=%d apdu.resplen=%d sw1=%02x sw2=%02x",
r, apdu.resplen, apdu.sw1, apdu.sw2);
@ -911,11 +906,11 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx,
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
enumtag = piv_objects[priv->selected_obj].enumtag;
if (priv->rb_state == 1) {
if (priv->rwb_state == 1) {
r = 0;
}
if (priv->rb_state == -1) {
if (priv->rwb_state == -1) {
r = piv_get_cached_data(card, enumtag, &rbuf, &rbuflen);
if (r >=0) {
@ -947,7 +942,7 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx,
}
}
priv->rb_state = 0;
priv->rwb_state = 0;
}
if (priv->return_only_cert || piv_objects[enumtag].flags & PIV_OBJECT_TYPE_PUBKEY) {
@ -963,7 +958,7 @@ static int piv_read_binary(sc_card_t *card, unsigned int idx,
count = rbuflen - idx;
if (count <= 0) {
r = 0;
priv->rb_state = 1;
priv->rwb_state = 1;
} else {
memcpy(buf, rbuf + idx, count);
r = count;
@ -1011,8 +1006,9 @@ static int piv_put_data(sc_card_t *card, int tag,
SC_FUNC_RETURN(card->ctx, 1, r);
}
static int piv_write_certificate(sc_card_t *card,
unsigned idx, const u8* buf, size_t count,
const u8* buf, size_t count,
unsigned long flags) {
piv_private_data_t * priv = PIV_DATA(card);
int enumtag;
@ -1023,7 +1019,7 @@ static int piv_write_certificate(sc_card_t *card,
size_t taglen;
sc_debug(card->ctx,"DEE cert len=%d",count);
taglen = put_tag_and_len(0x70, count, NULL)
taglen = put_tag_and_len(0x70, count, NULL)
+ put_tag_and_len(0x71, 1, NULL)
+ put_tag_and_len(0xFE, 0, NULL);
@ -1039,7 +1035,7 @@ static int piv_write_certificate(sc_card_t *card,
memcpy(p, buf, count);
p += count;
put_tag_and_len(0x71, 1, &p);
*p++ = (flags && 1)? 0x80:0x00; /* certinfo, i.e. gziped? */
*p++ = (flags)? 0x80:0x00; /* certinfo, i.e. gziped? */
put_tag_and_len(0xFE,0,&p); /* LRC tag */
sc_debug(card->ctx,"DEE buf %p len %d %d", sbuf, p -sbuf, sbuflen);
@ -1051,32 +1047,99 @@ static int piv_write_certificate(sc_card_t *card,
SC_FUNC_RETURN(card->ctx, 1, r);
}
/*
* We need to add the 0x53 tag and other specific tags,
* For certs we need to add the 0x53 tag and other specific tags,
* and call the piv_put_data
* Note: the select file will have saved the object type for us
* Write is only used by piv-tool, so we will use flags==1
* to indicate we are writing a compressed cert.
* Write is only used by piv-tool, so we will use flags:
* length << 8 | compress < 1 | cert
* to indicate we are writing a cert and if is compressed.
* if its not a cert its an object.
*
* Therefore when idx=0, we will get the length of the object
* and allocate a buffer, so we can support partial writes.
* When the last chuck of the data is sent, we will write it.
*/
static int piv_write_binary(sc_card_t *card, unsigned int idx,
const u8 *buf, size_t count, unsigned long flags)
{
piv_private_data_t * priv = PIV_DATA(card);
int r;
int enumtag;
SC_FUNC_CALLED(card->ctx,1);
if (priv->selected_obj < 0)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
if (idx != 0)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NO_CARD_SUPPORT);
if (piv_objects[priv->selected_obj].flags & PIV_OBJECT_TYPE_CERT) {
SC_FUNC_RETURN(card->ctx, 1, piv_write_certificate(card, idx, buf, count, flags));
} else {
sc_debug(card->ctx, "Don't know how to write object %s\n",
piv_objects[priv->selected_obj].name);
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NOT_SUPPORTED);
enumtag = piv_objects[priv->selected_obj].enumtag;
if (priv->rwb_state == 1) /* trying to write at end */
SC_FUNC_RETURN(card->ctx, 1, 0);
if (priv->rwb_state == -1) {
/* if cached, remove old entry */
if (priv->obj_cache[enumtag].flags & PIV_OBJ_CACHE_VALID) {
priv->obj_cache[enumtag].flags = 0;
if (priv->obj_cache[enumtag].obj_data) {
free(priv->obj_cache[enumtag].obj_data);
priv->obj_cache[enumtag].obj_data = NULL;
priv->obj_cache[enumtag].obj_len = 0;
}
if (priv->obj_cache[enumtag].internal_obj_data) {
free(priv->obj_cache[enumtag].internal_obj_data);
priv->obj_cache[enumtag].internal_obj_data = NULL;
priv->obj_cache[enumtag].internal_obj_len = 0;
}
}
if (idx != 0)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_NO_CARD_SUPPORT);
priv->w_buf_len = flags>>8;
if (priv->w_buf_len == 0)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_INTERNAL);
priv->w_buf = (u8 *)malloc(priv->w_buf_len);
priv-> rwb_state = 0;
}
/* on each pass make sure we have w_buf */
if (priv->w_buf == NULL)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY);
if (idx + count > priv->w_buf_len)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OBJECT_NOT_VALID);
memcpy(priv->w_buf + idx, buf, count); /* copy one chunk */
/* if this was not the last chunk, return to get rest */
if (idx + count < priv->w_buf_len)
SC_FUNC_RETURN(card->ctx, 1, count);
priv-> rwb_state = 1; /* at end of object */
if ( flags & 1) {
r = piv_write_certificate(card, priv->w_buf, priv->w_buf_len,
flags & 0x02);
} else {
r = piv_put_data(card, enumtag, priv->w_buf, priv->w_buf_len);
}
/* if it worked, will cache it */
if (r >= 0 && priv->w_buf) {
priv->obj_cache[enumtag].flags = PIV_OBJ_CACHE_VALID;
priv->obj_cache[enumtag].obj_data = priv->w_buf;
priv->obj_cache[enumtag].obj_len = priv->w_buf_len;
} else {
if (priv->w_buf)
free(priv->w_buf);
}
priv->w_buf = NULL;
priv->w_buf_len = 0;
SC_FUNC_RETURN(card->ctx, 1, (r < 0)? r : count);
}
/*
@ -1385,9 +1448,11 @@ err:
static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* serial)
{
int r;
int i;
u8 gbits;
u8 *rbuf = NULL;
u8 *body, *fascn;
size_t rbuflen = 0, bodylen, fascnlen;
u8 *body, *fascn, *guid;
size_t rbuflen = 0, bodylen, fascnlen, guidlen;
u8 temp[2000];
size_t templen = sizeof(temp);
@ -1396,6 +1461,11 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri
/* ensure we've got the PIV selected, and nothing else is in process */
/* This fixes several problems due to previous incomplete APDUs during card detection */
/* Note: We need the temp because (some?) Oberthur cards don't like selecting an applet without response data */
/* 800-73-3 part1 draft, and CIO Council docs imply for PIV Compatible card
* The FASC-N Agency code should be 9999 and there should be a GUID
* based on RFC 4122. RIf so and the GUID is not all 0's
* we will use the GUID as the serial number.
*/
piv_select_aid(card, piv_aids[0].value, piv_aids[0].len_short, temp, &templen);
r = piv_get_cached_data(card, PIV_OBJ_CHUI, &rbuf, &rbuflen);
@ -1406,16 +1476,35 @@ static int piv_get_serial_nr_from_CHUI(sc_card_t* card, sc_serial_number_t* seri
body = (u8 *)sc_asn1_find_tag(card->ctx, rbuf, rbuflen, 0x53, &bodylen); /* Pass the outer wrapper asn1 */
if (body != NULL && bodylen != 0) {
fascn = (u8 *)sc_asn1_find_tag(card->ctx, body, bodylen, 0x30, &fascnlen); /* Find the FASC-N data */
if (fascn != NULL && fascnlen != 0) {
serial->len = fascnlen < SC_MAX_SERIALNR ? fascnlen : SC_MAX_SERIALNR;
memcpy (serial->value, fascn, serial->len);
guid = (u8 *)sc_asn1_find_tag(card->ctx, body, bodylen, 0x34, &guidlen);
gbits = 0; /* if guid is valid, gbits will not be zero */
if (guid && guidlen == 16) {
for (i = 0; i < 16; i++) {
gbits = gbits | guid[i]; /* if all are zero, gbits will be zero */
}
}
sc_debug(card->ctx,"fascn=%p,fascnlen=%d,guid=%p,guidlen=%d,gbits=%2.2x\n",
fascn, fascnlen, guid, guidlen, gbits);
if (fascn && fascnlen == 25) {
/* test if guid and the fascn starts with ;9999 (in ISO 4bit + partiy code) */
if (!(gbits && fascn[0] == 0xD4 && fascn[1] == 0xE7
&& fascn[2] == 0x39 && (fascn[3] | 0x7F) == 0xFF)) {
serial->len = fascnlen < SC_MAX_SERIALNR ? fascnlen : SC_MAX_SERIALNR;
memcpy (serial->value, fascn, serial->len);
r = SC_SUCCESS;
gbits = 0; /* set to skip using guid below */
}
}
if (guid && gbits) {
serial->len = guidlen < SC_MAX_SERIALNR ? guidlen : SC_MAX_SERIALNR;
memcpy (serial->value, guid, serial->len);
r = SC_SUCCESS;
}
}
}
// if (rbuf != NULL)
// free (rbuf);
SC_FUNC_RETURN(card->ctx, 1, r);
}
@ -1682,7 +1771,7 @@ static int piv_select_file(sc_card_t *card, const sc_path_t *in_path,
priv->return_only_cert = (pathlen == 4 && path[2] == 0xce && path[3] == 0xce);
priv->selected_obj = i;
priv->rb_state = -1;
priv->rwb_state = -1;
/* make it look like the file was found. */
/* We don't want to read it now unless we need the length */
@ -1734,6 +1823,8 @@ static int piv_finish(sc_card_t *card)
if (priv) {
if (priv->aid_file)
sc_file_free(priv->aid_file);
if (priv->w_buf)
free(priv->w_buf);
for (i = 0; i < PIV_OBJ_LAST_ENUM - 1; i++) {
sc_debug(card->ctx,"DEE freeing #%d, %p:%d %p:%d", i,
priv->obj_cache[i].obj_data, priv->obj_cache[i].obj_len,
@ -1778,12 +1869,9 @@ static int piv_init(sc_card_t *card)
SC_FUNC_RETURN(card->ctx, 1, SC_ERROR_OUT_OF_MEMORY);
priv->aid_file = sc_file_new();
priv->selected_obj = -1;
priv->max_recv_size = 256;
/* priv->max_recv_size = 256; */
/* priv->max_recv_size = card->max_recv_size; */
priv->max_send_size = card->max_send_size;
/* TODO fix read_binary and write_binary (read_binary is fixed) */
card->max_recv_size = 0xffff; /* must force pkcs15 read_binary in one call */
card->max_send_size = 0xffff;
/* priv->max_send_size = card->max_send_size; */
sc_debug(card->ctx, "Max send = %d recv = %d\n",
card->max_send_size, card->max_recv_size);

View File

@ -2,7 +2,7 @@
* piv-tool.c: Tool for accessing smart cards with libopensc
*
* Copyright (C) 2001 Juha Yrjölä <juha.yrjola@iki.fi>
* Copyright (C) 2005, Douglas E. Engert <deengert@anl.gov>
* Copyright (C) 2005,2010 Douglas E. Engert <deengert@anl.gov>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@ -59,11 +59,12 @@ static const struct option options[] = {
{ "admin", 0, NULL, 'A' },
{ "usepin", 0, NULL, 'P' }, /* some beta cards want user pin for put_data */
{ "genkey", 0, NULL, 'G' },
{ "cert", 0, NULL, 'C' },
{ "compresscert", 0, NULL, 'Z' },
{ "object", 1, NULL, 'O' },
{ "cert", 1, NULL, 'C' },
{ "compresscert", 1, NULL, 'Z' },
{ "req", 0, NULL, 'R' },
{ "out", 0, NULL, 'o' },
{ "in", 0, NULL, 'o' },
{ "out", 1, NULL, 'o' },
{ "in", 1, NULL, 'i' },
{ "send-apdu", 1, NULL, 's' },
{ "reader", 1, NULL, 'r' },
{ "card-driver", 1, NULL, 'c' },
@ -78,6 +79,7 @@ static const char *option_help[] = {
"authenticate using default 3des key",
"authenticate using user pin",
"Generate key <ref>:<alg> 9A:06 on card, and output pubkey",
"Load an object <containerID> containerID as defined in 800-73 without leading 0x",
"Load a cert <ref> where <ref> is 9A,9B,9C or 9D",
"Load a cert that has been gziped <ref>",
"Generate a cert req",
@ -95,6 +97,55 @@ static sc_card_t *card = NULL;
static BIO * bp = NULL;
static RSA * newkey = NULL;
static int load_object(const char * object_id, const char * object_file)
{
FILE *fp;
sc_path_t path;
size_t derlen;
u8 *der = NULL;
u8 *body;
size_t bodylen;
int r;
struct stat stat_buf;
if((fp=fopen(object_file, "r"))==NULL){
printf("Cannot open object file, %s %s\n",
(object_file)?object_file:"", strerror(errno));
return -1;
}
stat(object_file, &stat_buf);
derlen = stat_buf.st_size;
der = malloc(derlen);
if (der == NULL) {
printf("file %s is too big, %lu\n",
object_file, (unsigned long)derlen);
return-1 ;
}
if (1 != fread(der, derlen, 1, fp)) {
printf("unable to read file %s\n",object_file);
return -1;
}
/* check if tag and length are valid */
body = (u8 *)sc_asn1_find_tag(card->ctx, der, derlen, 0x53, &bodylen);
if (body == NULL || derlen != body - der + bodylen) {
fprintf(stderr, "object tag or length not valid\n");
return -1;
}
sc_format_path(object_id, &path);
r = sc_select_file(card, &path, NULL);
if (r < 0) {
fprintf(stderr, "select file failed\n");
return -1;
}
/* leave 8 bits for flags, and pass in total length */
r = sc_write_binary(card, 0, der, derlen, derlen<<8);
return r;
}
static int load_cert(const char * cert_id, const char * cert_file,
int compress)
@ -111,7 +162,7 @@ static int load_cert(const char * cert_id, const char * cert_file,
if((fp=fopen(cert_file, "r"))==NULL){
printf("Cannot open cert file, %s %s\n",
cert_file, strerror(errno));
cert_file?cert_file:"", strerror(errno));
return -1;
}
if (compress) { /* file is gziped already */
@ -160,8 +211,9 @@ static int load_cert(const char * cert_id, const char * cert_file,
fprintf(stderr, "select file failed\n");
return -1;
}
/* we pass compress as the flag to card-piv.c write_binary */
r = sc_write_binary(card, 0, der, derlen, compress);
/* we pass length and 8 bits of flag to card-piv.c write_binary */
/* pass in its a cert and if needs compress */
r = sc_write_binary(card, 0, der, derlen, derlen<<8 | compress<1 | 1);
return r;
@ -359,6 +411,7 @@ int main(int argc, char * const argv[])
int do_admin_mode = 0;
int do_gen_key = 0;
int do_load_cert = 0;
int do_load_object = 0;
int compress_cert = 0;
int do_req = 0;
int do_print_serial = 0;
@ -368,6 +421,7 @@ int main(int argc, char * const argv[])
const char *out_file = NULL;
const char *in_file = NULL;
const char *cert_id = NULL;
const char *object_id = NULL;
const char *key_info = NULL;
const char *admin_info = NULL;
@ -375,7 +429,7 @@ int main(int argc, char * const argv[])
setbuf(stdout, NULL);
while (1) {
c = getopt_long(argc, argv, "nA:G:Z:C:Ri:o:fvs:c:w", options, &long_optind);
c = getopt_long(argc, argv, "nA:G:O:Z:C:Ri:o:fvs:c:w", options, &long_optind);
if (c == -1)
break;
if (c == '?')
@ -408,6 +462,11 @@ int main(int argc, char * const argv[])
key_info = optarg;
action_count++;
break;
case 'O':
do_load_object = 1;
object_id = optarg;
action_count++;
break;
case 'Z':
compress_cert = 1;
case 'C':
@ -494,6 +553,11 @@ int main(int argc, char * const argv[])
goto end;
action_count--;
}
if (do_load_object) {
if ((err = load_object(object_id, in_file)))
goto end;
action_count--;
}
if (do_load_cert) {
if ((err = load_cert(cert_id, in_file, compress_cert)))
goto end;