1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

A bunch of changes for the openpgp card.

This commit is contained in:
Werner Koch 2004-04-27 08:23:45 +00:00
parent 0c67c75cbe
commit 577d9c2342
19 changed files with 1958 additions and 263 deletions

View File

@ -1,3 +1,63 @@
2004-04-27 Werner Koch <wk@gnupg.org>
* g10.c: New commands --allow-admin and --deny-admin.
* options.h (opt): Add member ALLOW_ADMIN.
* tlv.h, tlv.c: New. Copied from gnupg-1.9.
* cardglue.c (open_card): The serialno is now set internally by
app_select_openpgp; changed invocation.
* cardglue.h (app_t, ctrl_t): New.
(GPG_ERR_EBUSY, GPG_ERR_ENOENT, GPG_ERR_NOT_FOUND, GPG_ERR_BUG)
(GPG_ERR_NOT_IMPLEMENTED, GPG_ERR_EACCESS): New.
(gpg_err_code_from_errno): New.
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h
* apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
* card-util.c: Updated from current gnupg-1.9.
Changes are:
* app-common.h: New members FNC.DEINIT and APP_LOCAL.
* app-openpgp.c (do_deinit): New.
(get_cached_data, flush_cache_item, flush_cache_after_error)
(flush_cache): New.
(get_one_do): Replaced arg SLOT by APP. Make used of cached data.
(verify_chv2, verify_chv3): Flush some cache item after error.
(do_change_pin): Ditto.
(do_sign): Ditto.
(do_setattr): Flush cache item.
(do_genkey): Flush the entire cache.
(compare_fingerprint): Use cached data.
* apdu.c (apdu_send_le): Reinitialize RESULTLEN. Handle
SW_EOF_REACHED like SW_SUCCESS.
* ccid-driver.c (parse_ccid_descriptor): Store some of the reader
features away. New arg HANDLE
(read_device_info): New arg HANDLE. Changed caller.
(bulk_in): Handle time extension requests.
(ccid_get_atr): Setup parameters and the IFSD.
(compute_edc): New. Factored out code.
(ccid_transceive): Use default NADs when required.
* apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
* iso7816.c (map_sw): Map it.
* ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
* apdu.c (apdu_get_status): New.
(ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
(get_status_ccid): New.
(apdu_reset): New.
(reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
(reset_ccid_reader): New.
(apdu_enum_reader): New.
* apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
(new_reader_slot) [USE_GNU_PTH]: Init mutex.
(apdu_reset, apdu_get_status, apdu_send_le): Run functions
in locked mode.
2004-04-25 David Shaw <dshaw@jabberwocky.com>
* getkey.c (get_seckey_byname2): Significantly simplify this
@ -131,6 +191,11 @@
* options.h: Encapsulate keyserver details. Change all callers.
2004-04-05 Werner Koch <wk@gnupg.org>
* status.h (STATUS_NEWSIG): New.
* status.c (get_status_string): Add it.
2004-03-27 David Shaw <dshaw@jabberwocky.com>
* keyedit.c (keyedit_menu): Request a trustdb update when adding a

View File

@ -78,7 +78,7 @@ card_support_source_scd = \
iso7816.c iso7816.h \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h
card_support_source_local = cardglue.c cardglue.h
card_support_source_local = cardglue.c cardglue.h tlv.c tlv.h
else
card_support_source_g10 =
card_support_source_scd =

View File

@ -1,5 +1,5 @@
/* apdu.c - ISO 7816 APDU functions and low level I/O
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -24,11 +24,18 @@
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#ifdef USE_GNU_PTH
# include <pth.h>
# include <unistd.h>
# include <fcntl.h>
#endif
#ifdef HAVE_OPENSC
# include <opensc/opensc.h>
#endif
#if GNUPG_MAJOR_VERSION == 1
#if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
@ -46,6 +53,11 @@
#include "dynload.h"
#include "ccid-driver.h"
#ifdef USE_GNU_PTH
#define NEED_PCSC_WRAPPER 1
#endif
#define MAX_READER 4 /* Number of readers we support concurrently. */
#define CARD_CONNECT_TIMEOUT 1 /* Number of seconds to wait for
insertion of the card (1 = don't wait). */
@ -57,6 +69,12 @@
#define DLSTDCALL
#endif
#ifdef _POSIX_OPEN_MAX
#define MAX_OPEN_FDS _POSIX_OPEN_MAX
#else
#define MAX_OPEN_FDS 20
#endif
/* A structure to collect information pertaining to one reader
slot. */
@ -72,6 +90,11 @@ struct reader_table_s {
unsigned long context;
unsigned long card;
unsigned long protocol;
#ifdef NEED_PCSC_WRAPPER
int req_fd;
int rsp_fd;
pid_t pid;
#endif /*NEED_PCSC_WRAPPER*/
} pcsc;
#ifdef HAVE_OPENSC
int is_osc; /* We are using the OpenSC driver layer. */
@ -83,6 +106,11 @@ struct reader_table_s {
int status;
unsigned char atr[33];
size_t atrlen;
unsigned int change_counter;
#ifdef USE_GNU_PTH
int lock_initialized;
pth_mutex_t lock;
#endif
};
typedef struct reader_table_s *reader_table_t;
@ -183,11 +211,27 @@ new_reader_slot (void)
log_error ("new_reader_slot: out of slots\n");
return -1;
}
#ifdef USE_GNU_PTH
if (!reader_table[reader].lock_initialized)
{
if (!pth_mutex_init (&reader_table[reader].lock))
{
log_error ("error initializing mutex: %s\n", strerror (errno));
return -1;
}
reader_table[reader].lock_initialized = 1;
}
#endif /*USE_GNU_PTH*/
reader_table[reader].used = 1;
reader_table[reader].is_ccid = 0;
reader_table[reader].is_ctapi = 0;
#ifdef HAVE_OPENSC
reader_table[reader].is_osc = 0;
#endif
#ifdef NEED_PCSC_WRAPPER
reader_table[reader].pcsc.req_fd = -1;
reader_table[reader].pcsc.rsp_fd = -1;
reader_table[reader].pcsc.pid = (pid_t)(-1);
#endif
return reader;
}
@ -368,6 +412,18 @@ close_ct_reader (int slot)
return 0;
}
static int
reset_ct_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
}
static int
ct_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
@ -395,6 +451,66 @@ ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifdef NEED_PCSC_WRAPPER
static int
writen (int fd, const void *buf, size_t nbytes)
{
size_t nleft = nbytes;
int nwritten;
/* log_printhex (" writen:", buf, nbytes); */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
nwritten = pth_write (fd, buf, nleft);
#else
nwritten = write (fd, buf, nleft);
#endif
if (nwritten < 0 && errno == EINTR)
continue;
if (nwritten < 0)
return -1;
nleft -= nwritten;
buf = (const char*)buf + nwritten;
}
return 0;
}
/* Read up to BUFLEN bytes from FD and return the number of bytes
actually read in NREAD. Returns -1 on error or 0 on success. */
static int
readn (int fd, void *buf, size_t buflen, size_t *nread)
{
size_t nleft = buflen;
int n;
/* void *orig_buf = buf; */
while (nleft > 0)
{
#ifdef USE_GNU_PTH
n = pth_read (fd, buf, nleft);
#else
n = read (fd, buf, nleft);
#endif
if (n < 0 && errno == EINTR)
continue;
if (n < 0)
return -1; /* read error. */
if (!n)
break; /* EOF */
nleft -= n;
buf = (char*)buf + n;
}
if (nread)
*nread = buflen - nleft;
/* log_printhex (" readn:", orig_buf, *nread); */
return 0;
}
#endif /*NEED_PCSC_WRAPPER*/
static const char *
pcsc_error_string (long err)
{
@ -455,6 +571,172 @@ pcsc_error_string (long err)
static int
open_pcsc_reader (const char *portstr)
{
#ifdef NEED_PCSC_WRAPPER
/* Open the PC/SC reader using the pcsc_wrapper program. This is
needed to cope with different thread models and other peculiarities
of libpcsclite. */
int slot;
reader_table_t slotp;
int fd, rp[2], wp[2];
int n, i;
pid_t pid;
size_t len;
unsigned char msgbuf[9];
int err;
slot = new_reader_slot ();
if (slot == -1)
return -1;
slotp = reader_table + slot;
/* Fire up the pcsc wrapper. We don't use any fork/exec code from
the common directy but implement it direclty so that this file
may still be source copied. */
if (pipe (rp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
slotp->used = 0;
return -1;
}
if (pipe (wp) == -1)
{
log_error ("error creating a pipe: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
slotp->used = 0;
return -1;
}
pid = fork ();
if (pid == -1)
{
log_error ("error forking process: %s\n", strerror (errno));
close (rp[0]);
close (rp[1]);
close (wp[0]);
close (wp[1]);
slotp->used = 0;
return -1;
}
slotp->pcsc.pid = pid;
if (!pid)
{ /*
=== Child ===
*/
/* Double fork. */
pid = fork ();
if (pid == -1)
_exit (31);
if (pid)
_exit (0); /* Immediate exit this parent, so that the child
gets cleaned up by the init process. */
/* Connect our pipes. */
if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
/* Send stderr to the bit bucket. */
fd = open ("/dev/null", O_WRONLY);
if (fd == -1)
log_fatal ("can't open `/dev/null': %s", strerror (errno));
if (fd != 2 && dup2 (fd, 2) == -1)
log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
/* Close all other files. */
n = sysconf (_SC_OPEN_MAX);
if (n < 0)
n = MAX_OPEN_FDS;
for (i=3; i < n; i++)
close(i);
errno = 0;
execl (GNUPG_LIBDIR "/pcsc-wrapper",
"pcsc-wrapper",
"--",
"1", /* API version */
opt.pcsc_driver, /* Name of the PC/SC library. */
NULL);
_exit (31);
}
/*
=== Parent ===
*/
close (wp[0]);
close (rp[1]);
slotp->pcsc.req_fd = wp[1];
slotp->pcsc.rsp_fd = rp[0];
/* Wait for the intermediate child to terminate. */
while ( (i=pth_waitpid (pid, NULL, 0)) == -1 && errno == EINTR)
;
/* Now send the open request. */
msgbuf[0] = 0x01; /* OPEN command. */
len = portstr? strlen (portstr):0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
{
log_error ("error sending PC/SC OPEN request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
if (len > DIM (slotp->atr))
{
log_error ("PC/SC returned a too large ATR (len=%x)\n", len);
goto command_failed;
}
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
{
log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
goto command_failed;
}
n = len;
if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
{
log_error ("error receiving PC/SC OPEN response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
slotp->atrlen = len;
dump_reader_status (slot);
return slot;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return -1;
#else /*!NEED_PCSC_WRAPPER */
long err;
int slot;
char *list = NULL;
@ -557,9 +839,16 @@ open_pcsc_reader (const char *portstr)
dump_reader_status (slot);
return slot;
#endif /*!NEED_PCSC_WRAPPER */
}
static int
pcsc_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: CT API error code. */
@ -567,6 +856,109 @@ static int
pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen)
{
#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len, full_len;
int i, n;
unsigned char msgbuf[9];
if (DBG_CARD_IO)
log_printhex (" PCSC_data:", apdu, apdulen);
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
return -1;
}
msgbuf[0] = 0x03; /* TRANSMIT command. */
len = apdulen;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
|| writen (slotp->pcsc.req_fd, apdu, len))
{
log_error ("error sending PC/SC TRANSMIT request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
{
log_error ("pcsc_transmit failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
return -1;
}
full_len = len;
n = *buflen < len ? *buflen : len;
if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
*buflen = n;
full_len -= len;
if (full_len)
{
log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
err = -1;
}
/* We need to read any rest of the response, to keep the
protocol runnng. */
while (full_len)
{
unsigned char dummybuf[128];
n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
{
log_error ("error receiving PC/SC TRANSMIT response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
full_len -= n;
}
return err;
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return -1;
#else /*!NEED_PCSC_WRAPPER*/
long err;
struct pcsc_io_request_s send_pci;
unsigned long recv_len;
@ -589,14 +981,87 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
pcsc_error_string (err), err);
return err? -1:0; /* FIXME: Return appropriate error code. */
#endif /*!NEED_PCSC_WRAPPER*/
}
static int
close_pcsc_reader (int slot)
{
#ifdef NEED_PCSC_WRAPPER
long err;
reader_table_t slotp;
size_t len;
int i;
unsigned char msgbuf[9];
slotp = reader_table + slot;
if (slotp->pcsc.req_fd == -1
|| slotp->pcsc.rsp_fd == -1
|| slotp->pcsc.pid == (pid_t)(-1) )
{
log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
return 0;
}
msgbuf[0] = 0x02; /* CLOSE command. */
len = 0;
msgbuf[1] = (len >> 24);
msgbuf[2] = (len >> 16);
msgbuf[3] = (len >> 8);
msgbuf[4] = (len );
if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
{
log_error ("error sending PC/SC CLOSE request: %s\n",
strerror (errno));
goto command_failed;
}
/* Read the response. */
if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
{
log_error ("error receiving PC/SC CLOSE response: %s\n",
i? strerror (errno) : "premature EOF");
goto command_failed;
}
len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
if (msgbuf[0] != 0x81 || len < 4)
{
log_error ("invalid response header from PC/SC received\n");
goto command_failed;
}
len -= 4; /* Already read the error code. */
err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
if (err)
log_error ("pcsc_close failed: %s (0x%lx)\n",
pcsc_error_string (err), err);
/* We will the wrapper in any case - errors are merely
informational. */
command_failed:
close (slotp->pcsc.req_fd);
close (slotp->pcsc.rsp_fd);
slotp->pcsc.req_fd = -1;
slotp->pcsc.rsp_fd = -1;
kill (slotp->pcsc.pid, SIGTERM);
slotp->pcsc.pid = (pid_t)(-1);
slotp->used = 0;
return 0;
#else /*!NEED_PCSC_WRAPPER*/
pcsc_release_context (reader_table[slot].pcsc.context);
reader_table[slot].used = 0;
return 0;
#endif /*!NEED_PCSC_WRAPPER*/
}
static int
reset_pcsc_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
}
@ -659,6 +1124,46 @@ close_ccid_reader (int slot)
}
static int
reset_ccid_reader (int slot)
{
int err;
reader_table_t slotp = reader_table + slot;
unsigned char atr[33];
size_t atrlen;
err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
if (err)
return -1;
/* If the reset was successful, update the ATR. */
assert (sizeof slotp->atr >= sizeof atr);
slotp->atrlen = atrlen;
memcpy (slotp->atr, atr, atrlen);
dump_reader_status (slot);
return 0;
}
static int
get_status_ccid (int slot, unsigned int *status)
{
int rc;
int bits;
rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
if (rc)
return -1;
if (bits == 0)
*status = 1|2|4;
else if (bits == 1)
*status = 2;
else
*status = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
@ -796,6 +1301,18 @@ close_osc_reader (int slot)
return 0;
}
static int
reset_osc_reader (int slot)
{
return SW_HOST_NOT_SUPPORTED;
}
static int
ocsc_get_status (int slot, unsigned int *status)
{
return SW_HOST_NOT_SUPPORTED;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
@ -894,6 +1411,45 @@ osc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
Driver Access
*/
static int
lock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
{
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static int
trylock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
{
if (errno == EBUSY)
return SW_HOST_BUSY;
log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
return SW_HOST_LOCKING_FAILED;
}
#endif /*USE_GNU_PTH*/
return 0;
}
static void
unlock_slot (int slot)
{
#ifdef USE_GNU_PTH
if (!pth_mutex_release (&reader_table[slot].lock))
log_error ("failed to release apdu lock: %s\n", strerror (errno));
#endif /*USE_GNU_PTH*/
}
/* Open the reader and return an internal slot number or -1 on
error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
the first USB reader. For PC/SC the first listed reader). If
@ -935,7 +1491,7 @@ apdu_open_reader (const char *portstr)
handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
if (!handle)
{
log_error ("apdu_open_reader: failed to open driver: %s",
log_error ("apdu_open_reader: failed to open driver: %s\n",
dlerror ());
return -1;
}
@ -957,12 +1513,13 @@ apdu_open_reader (const char *portstr)
/* No ctAPI configured, so lets try the PC/SC API */
if (!pcsc_api_loaded)
{
#ifndef NEED_PCSC_WRAPPER
void *handle;
handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
if (!handle)
{
log_error ("apdu_open_reader: failed to open driver `%s': %s",
log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
opt.pcsc_driver, dlerror ());
return -1;
}
@ -1018,9 +1575,10 @@ apdu_open_reader (const char *portstr)
dlclose (handle);
return -1;
}
#endif /*!NEED_PCSC_WRAPPER*/
pcsc_api_loaded = 1;
}
return open_pcsc_reader (portstr);
}
@ -1044,6 +1602,47 @@ apdu_close_reader (int slot)
return close_pcsc_reader (slot);
}
/* Enumerate all readers and return information on whether this reader
is in use. The caller should start with SLOT set to 0 and
increment it with each call until an error is returned. */
int
apdu_enum_reader (int slot, int *used)
{
if (slot < 0 || slot >= MAX_READER)
return SW_HOST_NO_DRIVER;
*used = reader_table[slot].used;
return 0;
}
/* Do a reset for the card in reader at SLOT. */
int
apdu_reset (int slot)
{
int sw;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = lock_slot (slot)))
return sw;
if (reader_table[slot].is_ctapi)
sw = reset_ct_reader (slot);
#ifdef HAVE_LIBUSB
else if (reader_table[slot].is_ccid)
sw = reset_ccid_reader (slot);
#endif
#ifdef HAVE_OPENSC
else if (reader_table[slot].is_osc)
sw = reset_osc_reader (slot);
#endif
else
sw = reset_pcsc_reader (slot);
unlock_slot (slot);
return sw;
}
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
@ -1060,6 +1659,7 @@ apdu_get_atr (int slot, size_t *atrlen)
*atrlen = reader_table[slot].atrlen;
return buf;
}
static const char *
@ -1082,7 +1682,62 @@ error_string (int slot, long rc)
}
/* Dispatcher for the actual send_apdu fucntion. */
/* Retrieve the status for SLOT. The function does obnly wait fot the
card to become available if HANG is set to true. On success the
bits in STATUS will be set to
bit 0 = card present and usable
bit 1 = card present
bit 2 = card active
bit 3 = card access locked [not yet implemented]
For must application, tetsing bit 0 is sufficient.
CHANGED will receive the value of the counter tracking the number
of card insertions. This value may be used to detect a card
change.
*/
int
apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed)
{
int sw;
unsigned int s;
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
return sw;
if (reader_table[slot].is_ctapi)
sw = ct_get_status (slot, &s);
#ifdef HAVE_LIBUSB
else if (reader_table[slot].is_ccid)
sw = get_status_ccid (slot, &s);
#endif
#ifdef HAVE_OPENSC
else if (reader_table[slot].is_osc)
sw = osc_get_status (slot, &s);
#endif
else
sw = pcsc_get_status (slot, &s);
unlock_slot (slot);
if (sw)
return sw;
if (status)
*status = s;
if (changed)
*changed = reader_table[slot].change_counter;
return 0;
}
/* Dispatcher for the actual send_apdu function. Note, that this
function should be called in locked state. */
static int
send_apdu (int slot, unsigned char *apdu, size_t apdulen,
unsigned char *buffer, size_t *buflen)
@ -1117,13 +1772,18 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
int lc, const char *data, int le,
unsigned char **retbuf, size_t *retbuflen)
{
unsigned char result[256+10]; /* 10 extra in case of bugs in the driver. */
size_t resultlen = 256;
#define RESULTLEN 256
unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
the driver. */
size_t resultlen;
unsigned char apdu[5+256+1];
size_t apdulen;
int sw;
long rc; /* we need a long here due to PC/SC. */
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (DBG_CARD_IO)
log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
class, ins, p0, p1, lc, le);
@ -1135,6 +1795,9 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
if ((!data && lc != -1) || (data && lc == -1))
return SW_HOST_INV_VALUE;
if ((sw = lock_slot (slot)))
return sw;
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = ins;
@ -1151,11 +1814,13 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
assert (sizeof (apdu) >= apdulen);
/* As safeguard don't pass any garbage from the stack to the driver. */
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) failed: %s\n",
slot, error_string (slot, rc));
unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@ -1168,13 +1833,16 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen);
}
if (sw == SW_SUCCESS)
if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
{
if (retbuf)
{
*retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf)
return SW_HOST_OUT_OF_CORE;
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
@ -1190,7 +1858,10 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*retbuf = p = xtrymalloc (bufsize);
if (!*retbuf)
return SW_HOST_OUT_OF_CORE;
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
@ -1200,20 +1871,23 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
int len = (sw & 0x00ff);
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
if (DBG_CARD_IO)
log_debug ("apdu_send_simple(%d): %d more bytes available\n",
slot, len);
apdulen = 0;
apdu[apdulen++] = class;
apdu[apdulen++] = 0xC0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 0;
apdu[apdulen++] = 64; /* that is 256 bytes for Le */
apdu[apdulen++] = len;
memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
resultlen = RESULTLEN;
rc = send_apdu (slot, apdu, apdulen, result, &resultlen);
if (rc || resultlen < 2)
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, error_string (slot, rc));
unlock_slot (slot);
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
@ -1225,16 +1899,21 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
log_printhex (" dump: ", result, resultlen);
}
if ((sw & 0xff00) == SW_MORE_DATA || sw == SW_SUCCESS)
if ((sw & 0xff00) == SW_MORE_DATA
|| sw == SW_SUCCESS
|| sw == SW_EOF_REACHED )
{
if (retbuf)
if (retbuf && resultlen)
{
if (p - *retbuf + resultlen > bufsize)
{
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp)
return SW_HOST_OUT_OF_CORE;
{
unlock_slot (slot);
return SW_HOST_OUT_OF_CORE;
}
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
@ -1257,10 +1936,14 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
*retbuf = tmp;
}
}
unlock_slot (slot);
if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
log_printhex (" dump: ", *retbuf, *retbuflen);
return sw;
#undef RESULTLEN
}
/* Send an APDU to the card in SLOT. The APDU is created from all

View File

@ -27,13 +27,16 @@
enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/
SW_EOF_REACHED = 0x6282,
SW_EEPROM_FAILURE = 0x6581,
SW_WRONG_LENGTH = 0x6700,
SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985,
SW_NOT_SUPPORTED = 0x6a81,
SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
SW_NOT_SUPPORTED = 0x6a81,
SW_FILE_NOT_FOUND = 0x6a82,
SW_RECORD_NOT_FOUND = 0x6a83,
SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00,
@ -45,9 +48,12 @@ enum {
those values can't be issued by a card. */
SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
between errnos on a failed malloc. */
SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INV_VALUE = 0x10002,
SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
SW_HOST_NO_DRIVER = 0x10004
SW_HOST_NO_DRIVER = 0x10004,
SW_HOST_NOT_SUPPORTED = 0x10005,
SW_HOST_LOCKING_FAILED= 0x10006,
SW_HOST_BUSY = 0x10007
};
@ -55,10 +61,14 @@ enum {
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr);
int apdu_close_reader (int slot);
int apdu_enum_reader (int slot, int *used);
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
/* The apdu send functions do return status words. */
int apdu_reset (int slot);
int apdu_get_status (int slot, int hang,
unsigned int *status, unsigned int *changed);
int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data);
int apdu_send (int slot, int class, int ins, int p0, int p1,

View File

@ -21,6 +21,12 @@
#ifndef GNUPG_SCD_APP_COMMON_H
#define GNUPG_SCD_APP_COMMON_H
#if GNUPG_MAJOR_VERSION != 1
#include <ksba.h>
#endif
struct app_local_s; /* Defined by all app-*.c. */
struct app_ctx_s {
int initialized; /* The application has been initialied and the
function pointers may be used. Note that for
@ -29,111 +35,131 @@ struct app_ctx_s {
int slot; /* Used reader. */
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype;
unsigned int card_version;
int did_chv1;
int force_chv1; /* True if the card does not cache CHV1. */
int did_chv2;
int did_chv3;
struct app_local_s *app_local; /* Local to the application. */
struct {
int (*learn_status) (APP app, CTRL ctrl);
int (*getattr) (APP app, CTRL ctrl, const char *name);
int (*setattr) (APP app, const char *name,
void (*deinit) (app_t app);
int (*learn_status) (app_t app, ctrl_t ctrl);
int (*readcert) (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int (*getattr) (app_t app, ctrl_t ctrl, const char *name);
int (*setattr) (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
int (*sign) (APP app,
int (*sign) (app_t app,
const char *keyidstr, int hashalgo,
int (pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen );
int (*auth) (APP app, const char *keyidstr,
int (*auth) (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
int (*decipher) (APP app, const char *keyidstr,
int (*decipher) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
int (*genkey) (APP app, CTRL ctrl,
int (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*change_pin) (APP app, CTRL ctrl,
int (*change_pin) (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*check_pin) (APP app, const char *keyidstr,
int (*check_pin) (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg);
} fnc;
};
#if GNUPG_MAJOR_VERSION == 1
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
int app_select_openpgp (app_t app);
int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
#else
/*-- app-help.c --*/
gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
/*-- app.c --*/
void app_set_default_reader_port (const char *portstr);
APP select_application (void);
int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
int app_write_learn_status (APP app, CTRL ctrl);
int app_getattr (APP app, CTRL ctrl, const char *name);
int app_setattr (APP app, const char *name,
app_t select_application (ctrl_t ctrl, int slot, const char *name);
void release_application (app_t app);
int app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
int app_write_learn_status (app_t app, ctrl_t ctrl);
int app_readcert (app_t app, const char *certid,
unsigned char **cert, size_t *certlen);
int app_getattr (app_t app, ctrl_t ctrl, const char *name);
int app_setattr (app_t app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
int app_sign (APP app, const char *keyidstr, int hashalgo,
int app_sign (app_t app, const char *keyidstr, int hashalgo,
int (pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen );
int app_auth (APP app, const char *keyidstr,
int app_auth (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
int app_decipher (APP app, const char *keyidstr,
int app_decipher (app_t app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen );
int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
int app_genkey (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
int app_get_challenge (app_t app, size_t nbytes, unsigned char *buffer);
int app_change_pin (app_t app, ctrl_t ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_check_pin (APP app, const char *keyidstr,
int app_check_pin (app_t app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
/*-- app-openpgp.c --*/
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
int app_select_openpgp (app_t app);
int app_openpgp_cardinfo (APP app,
int app_openpgp_cardinfo (app_t app,
char **serialno,
char **disp_name,
char **pubkey_url,
unsigned char **fpr1,
unsigned char **fpr2,
unsigned char **fpr3);
int app_openpgp_storekey (APP app, int keyno,
int app_openpgp_storekey (app_t app, int keyno,
unsigned char *template, size_t template_len,
time_t created_at,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_openpgp_readkey (APP app, int keyno,
int app_openpgp_readkey (app_t app, int keyno,
unsigned char **m, size_t *mlen,
unsigned char **e, size_t *elen);
/*-- app-nks.c --*/
int app_select_nks (app_t app);
/*-- app-dinsig.c --*/
int app_select_dinsig (app_t app);
#endif

View File

@ -1,5 +1,5 @@
/* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -42,7 +42,7 @@
#include "iso7816.h"
#include "app-common.h"
#include "tlv.h"
static struct {
@ -50,121 +50,196 @@ static struct {
int constructed;
int get_from; /* Constructed DO with this DO or 0 for direct access. */
int binary;
int dont_cache;
int flush_on_error;
char *desc;
} data_objects[] = {
{ 0x005E, 0, 0, 1, "Login Data" },
{ 0x5F50, 0, 0, 0, "URL" },
{ 0x0065, 1, 0, 1, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, "Sex" },
{ 0x006E, 1, 0, 1, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, "AID" },
{ 0x0073, 1, 0, 1, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, "CA Fingerprints" },
{ 0x007A, 1, 0, 1, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, "Digital Signature Counter" },
{ 0x005E, 0, 0, 1, 0, 0, "Login Data" },
{ 0x5F50, 0, 0, 0, 0, 0, "URL" },
{ 0x0065, 1, 0, 1, 0, 0, "Cardholder Related Data"},
{ 0x005B, 0, 0x65, 0, 0, 0, "Name" },
{ 0x5F2D, 0, 0x65, 0, 0, 0, "Language preferences" },
{ 0x5F35, 0, 0x65, 0, 0, 0, "Sex" },
{ 0x006E, 1, 0, 1, 0, 0, "Application Related Data" },
{ 0x004F, 0, 0x6E, 1, 0, 0, "AID" },
{ 0x0073, 1, 0, 1, 0, 0, "Discretionary Data Objects" },
{ 0x0047, 0, 0x6E, 1, 0, 0, "Card Capabilities" },
{ 0x00C0, 0, 0x6E, 1, 0, 0, "Extended Card Capabilities" },
{ 0x00C1, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Signature" },
{ 0x00C2, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Decryption" },
{ 0x00C3, 0, 0x6E, 1, 0, 0, "Algorithm Attributes Authentication" },
{ 0x00C4, 0, 0x6E, 1, 0, 1, "CHV Status Bytes" },
{ 0x00C5, 0, 0x6E, 1, 0, 0, "Fingerprints" },
{ 0x00C6, 0, 0x6E, 1, 0, 0, "CA Fingerprints" },
{ 0x007A, 1, 0, 1, 0, 0, "Security Support Template" },
{ 0x0093, 0, 0x7A, 1, 1, 0, "Digital Signature Counter" },
{ 0 }
};
struct cache_s {
struct cache_s *next;
int tag;
size_t length;
unsigned char data[1];
};
struct app_local_s {
struct cache_s *cache;
};
static unsigned long convert_sig_counter_value (const unsigned char *value,
size_t valuelen);
static unsigned long get_sig_counter (APP app);
/* Locate a TLV encoded data object in BUFFER of LENGTH and
return a pointer to value as well as its length in NBYTES. Return
NULL if it was not found. Note, that the function does not check
whether the value fits into the provided buffer.
FIXME: Move this to an extra file, it is mostly duplicated from card.c.
*/
static const unsigned char *
find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
/* Deconstructor. */
static void
do_deinit (app_t app)
{
const unsigned char *s = buffer;
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
if (app && app->app_local)
{
buffer = s;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* we expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
struct cache_s *c, *c2;
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
for (c = app->app_local->cache; c; c = c2)
{
*nbytes = len;
return s;
c2 = c->next;
xfree (c);
}
if (len > n)
return NULL; /* buffer too short to skip to the next tag. */
s += len; n -= len;
xfree (app->app_local);
app->app_local = NULL;
}
}
/* Wrapper around iso7816_get_data which first tries to get the data
from the cache. */
static gpg_error_t
get_cached_data (app_t app, int tag,
unsigned char **result, size_t *resultlen)
{
gpg_error_t err;
int i;
unsigned char *p;
size_t len;
struct cache_s *c;
*result = NULL;
*resultlen = 0;
if (app->app_local)
{
for (c=app->app_local->cache; c; c = c->next)
if (c->tag == tag)
{
p = xtrymalloc (c->length);
if (!p)
return gpg_error (gpg_err_code_from_errno (errno));
memcpy (p, c->data, c->length);
*resultlen = c->length;
*result = p;
return 0;
}
}
err = iso7816_get_data (app->slot, tag, &p, &len);
if (err)
return err;
*result = p;
*resultlen = len;
/* Check whether we should cache this object. */
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag)
{
if (data_objects[i].dont_cache)
return 0;
break;
}
/* No, cache it. */
if (!app->app_local)
app->app_local = xtrycalloc (1, sizeof *app->app_local);
/* Note that we can safely ignore out of core errors. */
if (app->app_local)
{
for (c=app->app_local->cache; c; c = c->next)
assert (c->tag != tag);
c = xtrymalloc (sizeof *c + len);
if (c)
{
memcpy (c->data, p, len);
c->length = len;
c->tag = tag;
c->next = app->app_local->cache;
app->app_local->cache = c;
}
}
return 0;
}
/* Remove DO at TAG from the cache. */
static void
flush_cache_item (app_t app, int tag)
{
struct cache_s *c, *cprev;
int i;
if (!app->app_local)
return;
for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
if (c->tag == tag)
{
if (cprev)
cprev->next = c->next;
else
app->app_local->cache = c->next;
xfree (c);
for (c=app->app_local->cache; c ; c = c->next)
assert (c->tag != tag); /* Oops: duplicated entry. */
return;
}
/* Try again if we have an outer tag. */
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].tag == tag && data_objects[i].get_from
&& data_objects[i].get_from != tag)
flush_cache_item (app, data_objects[i].get_from);
}
/* Flush all entries from the cache which might be out of sync after
an error. */
static void
flush_cache_after_error (app_t app)
{
int i;
for (i=0; data_objects[i].tag; i++)
if (data_objects[i].flush_on_error)
flush_cache_item (app, data_objects[i].tag);
}
/* Flush the entire cache. */
static void
flush_cache (app_t app)
{
if (app && app->app_local)
{
struct cache_s *c, *c2;
for (c = app->app_local->cache; c; c = c2)
{
c2 = c->next;
xfree (c);
}
app->app_local->cache = NULL;
}
}
@ -174,7 +249,7 @@ find_tlv (const unsigned char *buffer, size_t length,
NULL if not found or a pointer which must be used to release the
buffer holding value. */
static void *
get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes)
{
int rc, i;
unsigned char *buffer;
@ -191,13 +266,13 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
rc = -1;
if (data_objects[i].tag && data_objects[i].get_from)
{
rc = iso7816_get_data (slot, data_objects[i].get_from,
&buffer, &buflen);
rc = get_cached_data (app, data_objects[i].get_from,
&buffer, &buflen);
if (!rc)
{
const unsigned char *s;
s = find_tlv (buffer, buflen, tag, &valuelen, 0);
s = find_tlv (buffer, buflen, tag, &valuelen);
if (!s)
value = NULL; /* not found */
else if (valuelen > buflen - (s - buffer))
@ -213,7 +288,7 @@ get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
if (!value) /* Not in a constructed DO, try simple. */
{
rc = iso7816_get_data (slot, tag, &buffer, &buflen);
rc = get_cached_data (app, tag, &buffer, &buflen);
if (!rc)
{
value = buffer;
@ -271,7 +346,7 @@ dump_all_do (int slot)
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv (buffer, buflen,
data_objects[j].tag, &valuelen, 0);
data_objects[j].tag, &valuelen);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
@ -333,7 +408,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
return out_of_core ();
return gpg_error (gpg_err_code_from_errno (errno));
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
@ -443,7 +518,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
{
/* The serial number is very special. We could have used the
AID DO to retrieve it, but we have it already in the app
context and the stanmp argument is required anyway which we
context and the stamp argument is required anyway which we
can't by other means. The AID DO is available anyway but not
hex formatted. */
char *serial;
@ -462,7 +537,7 @@ do_getattr (APP app, CTRL ctrl, const char *name)
return 0;
}
relptr = get_one_do (app->slot, table[idx].tag, &value, &valuelen);
relptr = get_one_do (app, table[idx].tag, &value, &valuelen);
if (relptr)
{
if (table[idx].special == 1)
@ -517,7 +592,7 @@ do_learn_status (APP app, CTRL ctrl)
/* Verify CHV2 if required. Depending on the configuration of the
card CHV1 will also be verified. */
static int
verify_chv2 (APP app,
verify_chv2 (app_t app,
int (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
@ -534,11 +609,19 @@ verify_chv2 (APP app,
return rc;
}
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV2) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
if (rc)
{
log_error ("verify CHV2 failed: %s\n", gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
}
app->did_chv2 = 1;
@ -552,6 +635,7 @@ verify_chv2 (APP app,
{
log_error ("verify CHV1 failed: %s\n", gpg_strerror (rc));
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
}
app->did_chv1 = 1;
@ -569,6 +653,12 @@ verify_chv3 (APP app,
{
int rc = 0;
if (!opt.allow_admin)
{
log_info ("access to admin commands is not configured\n");
return gpg_error (GPG_ERR_EACCES);
}
if (!app->did_chv3)
{
char *pinvalue;
@ -580,11 +670,19 @@ verify_chv3 (APP app,
return rc;
}
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV3) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
xfree (pinvalue);
if (rc)
{
log_error ("verify CHV3 failed: %s\n", gpg_strerror (rc));
flush_cache_after_error (app);
return rc;
}
app->did_chv3 = 1;
@ -629,6 +727,10 @@ do_setattr (APP app, const char *name,
if (rc)
return rc;
/* Flush the cache before writing it, so that the next get operation
will reread the data from the card and thus get synced in case of
errors (e.g. data truncated by the card). */
flush_cache_item (app, table[idx].tag);
rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
if (rc)
log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
@ -715,7 +817,8 @@ do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
pinvalue, strlen (pinvalue));
}
xfree (pinvalue);
if (rc)
flush_cache_after_error (app);
leave:
return rc;
@ -746,13 +849,17 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
return gpg_error (GPG_ERR_INV_ID);
keyno--;
/* We flush the cache to increase the traffic before a key
generation. This _might_ help a card to gather more entropy. */
flush_cache (app);
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
if (rc)
{
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
rc = gpg_error (GPG_ERR_GENERAL);
@ -779,6 +886,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave;
xfree (buffer); buffer = NULL;
#if 1
log_info ("please wait while key is being generated ...\n");
start_at = time (NULL);
@ -800,7 +908,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
}
log_info ("key generation completed (%d seconds)\n",
(int)(time (NULL) - start_at));
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
rc = gpg_error (GPG_ERR_CARD);
@ -808,7 +916,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
goto leave;
}
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
if (!m)
{
rc = gpg_error (GPG_ERR_CARD);
@ -818,7 +926,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
/* log_printhex ("RSA n:", m, mlen); */
send_key_data (ctrl, "n", m, mlen);
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
e = find_tlv (keydata, keydatalen, 0x0082, &elen);
if (!e)
{
rc = gpg_error (GPG_ERR_CARD);
@ -869,7 +977,7 @@ get_sig_counter (APP app)
size_t valuelen;
unsigned long ul;
relptr = get_one_do (app->slot, 0x0093, &value, &valuelen);
relptr = get_one_do (app, 0x0093, &value, &valuelen);
if (!relptr)
return 0;
ul = convert_sig_counter_value (value, valuelen);
@ -887,13 +995,13 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
assert (keyno >= 1 && keyno <= 3);
rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
rc = get_cached_data (app, 0x006E, &buffer, &buflen);
if (rc)
{
log_error ("error reading application data\n");
return gpg_error (GPG_ERR_GENERAL);
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
fpr = find_tlv (buffer, buflen, 0x00C5, &n);
if (!fpr || n != 60)
{
xfree (buffer);
@ -1035,11 +1143,19 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
return rc;
}
if (strlen (pinvalue) < 6)
{
log_error ("prassphrase (CHV1) is too short; minimum length is 6\n");
xfree (pinvalue);
return gpg_error (GPG_ERR_BAD_PIN);
}
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
if (rc)
{
log_error ("verify CHV1 failed\n");
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
}
app->did_chv1 = 1;
@ -1053,6 +1169,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
{
log_error ("verify CHV2 failed\n");
xfree (pinvalue);
flush_cache_after_error (app);
return rc;
}
app->did_chv2 = 1;
@ -1182,7 +1299,8 @@ do_decipher (APP app, const char *keyidstr,
rc = verify_chv2 (app, pincb, pincb_arg);
if (!rc)
rc = iso7816_decipher (app->slot, indata, indatalen, outdata, outdatalen);
rc = iso7816_decipher (app->slot, indata, indatalen, 0,
outdata, outdatalen);
return rc;
}
@ -1241,7 +1359,7 @@ do_check_pin (APP app, const char *keyidstr,
/* Select the OpenPGP application on the card in SLOT. This function
must be used before any other OpenPGP application functions. */
int
app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
app_select_openpgp (APP app)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int slot = app->slot;
@ -1253,10 +1371,17 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
app->apptype = "OPENPGP";
app->did_chv1 = 0;
app->did_chv2 = 0;
app->did_chv3 = 0;
/* The OpenPGP card returns the serial number as part of the
AID; because we prefer to use OpenPGP serial numbers, we
replace a possibly already set one from a EF.GDO with this
one. Note, that for current OpenPGP cards, no EF.GDO exists
and thus it won't matter at all. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
@ -1266,17 +1391,14 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
log_printhex ("", buffer, buflen);
}
if (sn)
{
*sn = buffer;
*snlen = buflen;
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
}
else
xfree (buffer);
app->card_version = buffer[6] << 8;
app->card_version |= buffer[7];
xfree (app->serialno);
app->serialno = buffer;
app->serialnolen = buflen;
buffer = NULL;
relptr = get_one_do (app->slot, 0x00C4, &buffer, &buflen);
relptr = get_one_do (app, 0x00C4, &buffer, &buflen);
if (!relptr)
{
log_error ("can't access CHV Status Bytes - invalid OpenPGP card?\n");
@ -1288,7 +1410,9 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
if (opt.verbose > 1)
dump_all_do (slot);
app->fnc.deinit = do_deinit;
app->fnc.learn_status = do_learn_status;
app->fnc.readcert = NULL;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
app->fnc.genkey = do_genkey;
@ -1340,7 +1464,7 @@ app_openpgp_cardinfo (APP app,
if (disp_name)
{
*disp_name = NULL;
relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
relptr = get_one_do (app, 0x005B, &value, &valuelen);
if (relptr)
{
*disp_name = make_printable_string (value, valuelen, 0);
@ -1351,7 +1475,7 @@ app_openpgp_cardinfo (APP app,
if (pubkey_url)
{
*pubkey_url = NULL;
relptr = get_one_do (app->slot, 0x5F50, &value, &valuelen);
relptr = get_one_do (app, 0x5F50, &value, &valuelen);
if (relptr)
{
*pubkey_url = make_printable_string (value, valuelen, 0);
@ -1365,7 +1489,7 @@ app_openpgp_cardinfo (APP app,
*fpr2 = NULL;
if (fpr3)
*fpr3 = NULL;
relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
relptr = get_one_do (app, 0x00C5, &value, &valuelen);
if (relptr && valuelen >= 60)
{
if (fpr1)
@ -1471,7 +1595,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
if (!keydata)
{
log_error ("response does not contain the public key data\n");
@ -1479,7 +1603,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
goto leave;
}
a = find_tlv (keydata, keydatalen, 0x0081, &alen, 0);
a = find_tlv (keydata, keydatalen, 0x0081, &alen);
if (!a)
{
log_error ("response does not contain the RSA modulus\n");
@ -1490,7 +1614,7 @@ app_openpgp_readkey (APP app, int keyno, unsigned char **m, size_t *mlen,
*m = xmalloc (alen);
memcpy (*m, a, alen);
a = find_tlv (keydata, keydatalen, 0x0082, &alen, 0);
a = find_tlv (keydata, keydatalen, 0x0082, &alen);
if (!e)
{
log_error ("response does not contain the RSA public exponent\n");

View File

@ -169,8 +169,7 @@ print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
static void
print_name (FILE *fp, const char *text, const char *name)
{
tty_fprintf (fp, text);
tty_fprintf (fp, "%s", text);
/* FIXME: tty_printf_utf8_string2 eats everything after and
including an @ - e.g. when printing an url. */
@ -192,7 +191,7 @@ print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
if (opt.with_colons)
fprintf (fp, "%s:", tag);
else
tty_fprintf (fp, text);
tty_fprintf (fp, "%s", text);
if (name && *name)
{
@ -446,6 +445,14 @@ change_name (void)
if (*p == ' ')
*p = '<';
if (strlen (isoname) > 39 )
{
tty_printf (_("Error: Combined name too long "
"(limit is %d characters).\n"), 39);
xfree (isoname);
return -1;
}
log_debug ("setting Name to `%s'\n", isoname);
rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
if (rc)
@ -468,6 +475,14 @@ change_url (void)
trim_spaces (url);
cpr_kill_prompt ();
if (strlen (url) > 254 )
{
tty_printf (_("Error: URL too long "
"(limit is %d characters).\n"), 254);
xfree (url);
return -1;
}
rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
if (rc)
log_error ("error setting URL: %s\n", gpg_strerror (rc));
@ -488,6 +503,14 @@ change_login (void)
trim_spaces (data);
cpr_kill_prompt ();
if (strlen (data) > 254 )
{
tty_printf (_("Error: Login data too long "
"(limit is %d characters).\n"), 254);
xfree (data);
return -1;
}
rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
if (rc)
log_error ("error setting login data: %s\n", gpg_strerror (rc));

View File

@ -262,7 +262,7 @@ open_card (void)
app = xcalloc (1, sizeof *app);
app->slot = slot;
rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
rc = app_select_openpgp (app);
if (rc && !opt.batch)
{
write_status_text (STATUS_CARDCTRL, "1");

View File

@ -63,8 +63,10 @@ struct agent_card_genkey_s {
struct app_ctx_s;
struct ctrl_ctx_s;
typedef struct app_ctx_s *APP;
typedef struct ctrl_ctx_s *CTRL;
typedef struct app_ctx_s *APP; /* deprecated. */
typedef struct app_ctx_s *app_t;
typedef struct ctrl_ctx_s *CTRL; /* deprecated. */
typedef struct ctrl_ctx_s *ctrl_t;
#define GPG_ERR_GENERAL G10ERR_GENERAL
@ -86,6 +88,15 @@ typedef struct ctrl_ctx_s *CTRL;
#define GPG_ERR_WRONG_CARD G10ERR_GENERAL
#define GPG_ERR_WRONG_SECKEY G10ERR_WRONG_SECKEY
#define GPG_ERR_PIN_NOT_SYNCED G10ERR_GENERAL
#define GPG_ERR_NOT_FOUND G10ERR_GENERAL
#define GPG_ERR_BUG G10ERR_GENERAL
#define GPG_ERR_NOT_IMPLEMENTED G10ERR_GENERAL
#define GPG_ERR_BAD_BER G10ERR_GENERAL
#define GPG_ERR_EOF (-1)
#define GPG_ERR_EBUSY G10ERR_GENERAL
#define GPG_ERR_ENOENT G10ERR_OPEN_FILE
#define GPG_ERR_EACCES G10ERR_UNSUPPORTED
typedef int gpg_error_t;
typedef int gpg_err_code_t;
@ -94,7 +105,7 @@ typedef int gpg_err_code_t;
#define gpg_err_code(n) (n)
#define gpg_strerror(n) g10_errstr ((n))
#define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
#define gpg_err_code_from_errno(n) (G10ERR_GENERAL)
/* We are not using it in a library, so we even let xtrymalloc
abort. Because we won't never return from these malloc functions,

View File

@ -1,5 +1,5 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* This file is part of GnuPG.
@ -87,16 +87,32 @@
#define DRVNAME "ccid-driver: "
#ifdef GNUPG_MAJOR_VERSION /* This source is used within GnuPG. */
/* Depending on how this source is used we either define our error
output to go to stderr or to the jnlib based logging functions. We
use the latter when GNUPG_MAJOR_VERSION is defines or when both,
GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined.
*/
#if defined(GNUPG_MAJOR_VERSION) \
|| (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
# if GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
#if defined(GNUPG_SCD_MAIN_HEADER)
# include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
# include "options.h"
# include "util.h"
# include "memory.h"
# include "cardglue.h"
# else /* This is the modularized GnuPG 1.9 or later. */
# include "scdaemon.h"
# endif
#endif
/* Disable all debugging output for now. */
#undef DBG_CARD_IO
#define DBG_CARD_IO 0
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t); } while (0)
@ -132,8 +148,6 @@
#endif /* This source not used by scdaemon. */
/* Define to print information pertaining the T=1 protocol. */
#undef DEBUG_T1 1
@ -171,9 +185,15 @@ struct ccid_driver_s {
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
int nonnull_nad;
int auto_ifsd;
int max_ifsd;
int ifsd;
};
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
int use_crc);
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno);
@ -207,13 +227,18 @@ set_msg_len (unsigned char *msg, unsigned int length)
Note, that this code is based on the one in lsusb.c of the
usb-utils package, I wrote on 2003-09-01. -wk. */
static int
parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
parse_ccid_descriptor (ccid_driver_t handle,
const unsigned char *buf, size_t buflen)
{
unsigned int i;
unsigned int us;
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
handle->nonnull_nad = 0;
handle->auto_ifsd = 0;
handle->max_ifsd = 32;
handle->ifsd = 0;
if (buflen < 54 || buf[0] < 54)
{
DEBUGOUT ("CCID device descriptor is too short\n");
@ -259,6 +284,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
handle->max_ifsd = us;
us = convert_le_u32(buf+32);
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
@ -307,9 +333,15 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
if ((us & 0x0100))
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
if ((us & 0x0200))
DEBUGOUT (" NAD value other than 0x00 accpeted\n");
{
DEBUGOUT (" NAD value other than 0x00 accepted\n");
handle->nonnull_nad = 1;
}
if ((us & 0x0400))
DEBUGOUT (" Auto IFSD exchange\n");
{
DEBUGOUT (" Auto IFSD exchange\n");
handle->auto_ifsd = 1;
}
if ((us & 0x00010000))
{
@ -376,7 +408,7 @@ parse_ccid_descriptor (const unsigned char *buf, size_t buflen)
that the device is usable for us. Returns 0 on success or an error
code. */
static int
read_device_info (struct usb_device *dev)
read_device_info (ccid_driver_t handle, struct usb_device *dev)
{
int cfg_no;
@ -401,8 +433,9 @@ read_device_info (struct usb_device *dev)
{
if (ifcdesc->extra)
{
if (!parse_ccid_descriptor (ifcdesc->extra,
ifcdesc->extralen))
if (!parse_ccid_descriptor (handle,
ifcdesc->extra,
ifcdesc->extralen))
return 0; /* okay. we can use it. */
}
}
@ -445,10 +478,22 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
dev->descriptor->idVendor, dev->descriptor->idProduct);
if (!readerno)
{
rc = read_device_info (dev);
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
free (*handle);
*handle = NULL;
goto leave;
}
rc = read_device_info (*handle, dev);
if (rc)
{
DEBUGOUT ("device not supported\n");
free (*handle);
*handle = NULL;
goto leave;
}
@ -456,6 +501,8 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave;
}
@ -466,16 +513,11 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
free (*handle);
*handle = NULL;
goto leave;
}
*handle = calloc (1, sizeof **handle);
if (!*handle)
{
DEBUGOUT ("out of memory\n");
rc = -1;
goto leave;
}
(*handle)->idev = idev;
idev = NULL;
/* FIXME: Do we need to get the endpoint addresses from the
@ -495,7 +537,7 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
usb_free_match (match);
if (!rc && !*handle)
rc = -1; /* In case we didn't enter the while lool at all. */
rc = -1; /* In case we didn't enter the while loop at all. */
return rc;
}
@ -579,6 +621,7 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
int i, rc;
size_t msglen;
retry:
rc = usb_bulk_read (handle->idev,
0x82,
buffer, length,
@ -615,6 +658,14 @@ bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
return -1;
}
if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
{
/* Card present and active, time extension requested. */
DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
buffer[7], buffer[8]);
goto retry;
}
DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
" data:", buffer[7], buffer[8], buffer[9] );
for (i=10; i < msglen; i++)
@ -682,7 +733,7 @@ ccid_poll (ccid_driver_t handle)
int
ccid_slot_status (ccid_driver_t handle)
ccid_slot_status (ccid_driver_t handle, int *statusbits)
{
int rc;
unsigned char msg[100];
@ -703,6 +754,7 @@ ccid_slot_status (ccid_driver_t handle)
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
if (rc)
return rc;
*statusbits = (msg[7] & 3);
return 0;
}
@ -714,8 +766,12 @@ ccid_get_atr (ccid_driver_t handle,
{
int rc;
unsigned char msg[100];
size_t msglen;
unsigned char *tpdu;
size_t msglen, tpdulen;
unsigned char seqno;
int use_crc = 0;
unsigned int edc;
int i;
msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */
@ -743,11 +799,135 @@ ccid_get_atr (ccid_driver_t handle,
*atrlen = n;
}
/* Setup parameters to select T=1. */
msg[0] = PC_to_RDR_SetParameters;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 1; /* Select T=1. */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
/* FIXME: Get those values from the ATR. */
msg[10]= 0x01; /* Fi/Di */
msg[11]= 0x10; /* LRC, direct convention. */
msg[12]= 0; /* Extra guardtime. */
msg[13]= 0x41; /* BWI/CWI */
msg[14]= 0; /* No clock stoppping. */
msg[15]= 254; /* IFSC */
msg[16]= 0; /* Does not support non default NAD values. */
set_msg_len (msg, 7);
msglen = 10 + 7;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Note that we ignore the error code on purpose. */
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters, seqno);
/* Send an S-Block with our maximun IFSD to the CCID. */
if (!handle->auto_ifsd)
{
tpdu = msg+10;
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
tpdu[2] = 1;
tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
tpdulen = 4;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 0;
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
#ifdef DEBUG_T1
fprintf (stderr, "T1: put %c-block seq=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
#endif
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
/* Fixme: The next line for the current Valgrid without support
for USB IOCTLs. */
memset (msg, 0, sizeof msg);
rc = bulk_in (handle, msg, sizeof msg, &msglen,
RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
tpdu = msg + 10;
tpdulen = msglen - 10;
if (tpdulen < 4)
{
DEBUGOUT ("cannot yet handle short blocks!\n");
return -1;
}
#ifdef DEBUG_T1
fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
((msg[11] & 0xc0) == 0x80)? 'R' :
(msg[11] & 0x80)? 'S' : 'I',
((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
);
#endif
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
{
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
return -1;
}
DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
}
return 0;
}
static unsigned int
compute_edc (const unsigned char *data, size_t datalen, int use_crc)
{
if (use_crc)
{
return 0x42; /* Not yet implemented. */
}
else
{
unsigned char crc = 0;
for (; datalen; datalen--)
crc ^= *data++;
return crc;
}
}
/*
Protocol T=1 overview
@ -806,17 +986,19 @@ ccid_transceive (ccid_driver_t handle,
unsigned char *resp, size_t maxresplen, size_t *nresp)
{
int rc;
unsigned char send_buffer[10+258], recv_buffer[10+258];
unsigned char send_buffer[10+259], recv_buffer[10+259];
const unsigned char *apdu;
size_t apdulen;
unsigned char *msg, *tpdu, *p;
size_t msglen, tpdulen, n;
size_t msglen, tpdulen, last_tpdulen, n;
unsigned char seqno;
int i;
unsigned char crc;
unsigned int edc;
int use_crc = 0;
size_t dummy_nresp;
int next_chunk = 1;
int sending = 1;
int retries = 0;
if (!nresp)
nresp = &dummy_nresp;
@ -839,7 +1021,8 @@ ccid_transceive (ccid_driver_t handle,
return -1; /* Invalid length. */
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
if (apdulen > 128 /* fixme: replace by ifsc */)
{
@ -850,12 +1033,11 @@ ccid_transceive (ccid_driver_t handle,
}
tpdu[2] = apdulen;
memcpy (tpdu+3, apdu, apdulen);
crc = 0;
for (i=0,p=tpdu; i < apdulen+3; i++)
crc ^= *p++;
tpdu[3+apdulen] = crc;
tpdulen = apdulen + 4;
tpdulen = 3 + apdulen;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
}
msg[0] = PC_to_RDR_XfrBlock;
@ -866,6 +1048,7 @@ ccid_transceive (ccid_driver_t handle,
msg[9] = 0; /* RFU */
set_msg_len (msg, tpdulen);
msglen = 10 + tpdulen;
last_tpdulen = tpdulen;
DEBUGOUT ("sending");
for (i=0; i < msglen; i++)
@ -913,7 +1096,7 @@ ccid_transceive (ccid_driver_t handle,
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
retries = 0;
if (sending)
{ /* last block sent was successful. */
handle->t1_ns ^= 1;
@ -924,13 +1107,15 @@ ccid_transceive (ccid_driver_t handle,
{ /* Reponse does not match our sequence number. */
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
continue;
}
@ -944,7 +1129,9 @@ ccid_transceive (ccid_driver_t handle,
{
if (n > maxresplen)
{
DEBUGOUT ("provided buffer too short for received data\n");
DEBUGOUT_2 ("provided buffer too short for received data "
"(%u/%u)\n",
(unsigned int)n, (unsigned int)maxresplen);
return -1;
}
@ -959,20 +1146,27 @@ ccid_transceive (ccid_driver_t handle,
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
tpdu[2] = 0;
tpdulen = 3;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
}
else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */
if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */
if (++retries > 3)
{
DEBUGOUT ("3 failed retries\n");
return -1;
}
msg = send_buffer;
tpdulen = last_tpdulen;
}
else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
{ /* Reponse does not match our sequence number. */
@ -981,6 +1175,7 @@ ccid_transceive (ccid_driver_t handle,
}
else if (sending)
{ /* Send next chunk. */
retries = 0;
msg = send_buffer;
next_chunk = 1;
handle->t1_ns ^= 1;
@ -993,6 +1188,7 @@ ccid_transceive (ccid_driver_t handle,
}
else
{ /* This is a S-block. */
retries = 0;
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
@ -1001,14 +1197,16 @@ ccid_transceive (ccid_driver_t handle,
unsigned char bwi = tpdu[3];
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
/* NAD: DAD=1, SAD=0 */
tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
tpdu[2] = 1;
tpdu[3] = bwi;
tpdulen = 4;
for (crc=0,i=0,p=tpdu; i < tpdulen; i++)
crc ^= *p++;
tpdu[tpdulen++] = crc;
edc = compute_edc (tpdu, tpdulen, use_crc);
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
}
else
@ -1028,6 +1226,7 @@ main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
unsigned int slotstat;
rc = ccid_open_reader (&ccid, 0);
if (rc)
@ -1041,7 +1240,7 @@ main (int argc, char **argv)
ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr);
rc = ccid_slot_status (ccid);
rc = ccid_slot_status (ccid, &slotstat);
if (rc)
return 1;

View File

@ -63,6 +63,7 @@ int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
int ccid_slot_status (ccid_driver_t handle, int *statusbits);
int ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp);

View File

@ -341,6 +341,8 @@ enum cmd_and_opt_values
octapiDriver,
opcscDriver,
oDisableCCID,
oAllowAdmin,
oDenyAdmin,
aTest
};
@ -521,6 +523,10 @@ static ARGPARSE_OPTS opts[] = {
{ oSetNotation, "notation-data", 2, "@" }, /* Alias */
{ oSigNotation, "sig-notation", 2, "@" },
{ oCertNotation, "cert-notation", 2, "@" },
#ifdef ENABLE_CARD_SUPPORT
{ oAllowAdmin, "allow-admin",0,N_("allow the use of admin card commands")},
{ oDenyAdmin, "deny-admin",0,"@"},
#endif
{ 302, NULL, 0, N_(
"@\n(See the man page for a complete listing of all commands and options)\n"
@ -1699,6 +1705,8 @@ main( int argc, char **argv )
case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
case oDisableCCID: opt.disable_ccid = 1; break;
case oAllowAdmin: opt.allow_admin = 1; break;
case oDenyAdmin: opt.allow_admin = 0; break;
#endif /* ENABLE_CARD_SUPPORT*/
case oArmor: opt.armor = 1; opt.no_armor=0; break;

View File

@ -1,5 +1,5 @@
/* iso7816.c - ISO 7816 commands
* Copyright (C) 2003 Free Software Foundation, Inc.
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@ -24,7 +24,9 @@
#include <stdlib.h>
#include <string.h>
#if GNUPG_MAJOR_VERSION == 1
#if defined(GNUPG_SCD_MAIN_HEADER)
#include GNUPG_SCD_MAIN_HEADER
#elif GNUPG_MAJOR_VERSION == 1
/* This is used with GnuPG version < 1.9. The code has been source
copied from the current GnuPG >= 1.9 and is maintained over
there. */
@ -47,10 +49,13 @@
#define CMD_RESET_RETRY_COUNTER 0x2C
#define CMD_GET_DATA 0xCA
#define CMD_PUT_DATA 0xDA
#define CMD_MSE 0x22
#define CMD_PSO 0x2A
#define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84
#define CMD_READ_BINARY 0xB0
#define CMD_READ_RECORD 0xB2
static gpg_error_t
map_sw (int sw)
@ -66,6 +71,8 @@ map_sw (int sw)
case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
@ -75,6 +82,9 @@ map_sw (int sw)
case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
default:
if ((sw & 0x010000))
ec = GPG_ERR_GENERAL; /* Should not happen. */
@ -91,18 +101,79 @@ map_sw (int sw)
apdu_open_reader (), AID is a buffer of size AIDLEN holding the
requested application ID. The function can't be used to enumerate
AIDs and won't return the AID on success. The return value is 0
for okay or GNUPG error code. Note that ISO error codes are
for okay or a GPG error code. Note that ISO error codes are
internally mapped. */
gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen)
{
static char const openpgp_aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int sw;
int p1 = 0x0C; /* No FCI to be returned. */
if (aidlen == sizeof openpgp_aid
&& !memcmp (aid, openpgp_aid, sizeof openpgp_aid))
p1 = 0; /* The current openpgp cards don't allow 0x0c. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, p1, aidlen, aid);
return map_sw (sw);
}
gpg_error_t
iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen)
{
int sw, p0, p1;
unsigned char tagbuf[2];
tagbuf[0] = (tag >> 8) & 0xff;
tagbuf[1] = tag & 0xff;
if (result || resultlen)
{
*result = NULL;
*resultlen = 0;
return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
}
else
{
p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
p1 = 0x0c; /* No FC return. */
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
p0, p1, 2, tagbuf );
return map_sw (sw);
}
return 0;
}
/* This is a private command currently only working for TCOS cards. */
gpg_error_t
iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
}
return map_sw (sw);
}
/* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
gpg_error_t
@ -134,7 +205,7 @@ iso7816_change_reference_data (int slot, int chvno,
buf = xtrymalloc (oldchvlen + newchvlen);
if (!buf)
return out_of_core ();
return gpg_error (gpg_err_code_from_errno (errno));
if (oldchvlen)
memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen);
@ -205,6 +276,23 @@ iso7816_put_data (int slot, int tag,
return map_sw (sw);
}
/* Manage Security Environment. This is a weird operation and there
is no easy abstraction for it. Furthermore, some card seem to have
a different interpreation of 7816-8 and thus we resort to let the
caller decide what to do. */
gpg_error_t
iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data, size_t datalen)
{
int sw;
if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 || !data || !datalen)
return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, datalen, data);
return map_sw (sw);
}
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
success 0 is returned and the data is availavle in a newly
@ -236,13 +324,14 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
}
/* Perform the security operation DECIPHER. On
success 0 is returned and the plaintext is available in a newly
allocated buffer stored at RESULT with its length stored at
RESULTLEN. */
/* Perform the security operation DECIPHER. PADIND is the padding
indicator to be used. It should be 0 if no padding is required, a
value of -1 suppresses the padding byte. On success 0 is returned
and the plaintext is available in a newly allocated buffer stored
at RESULT with its length stored at RESULTLEN. */
gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
int padind, unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buf;
@ -252,15 +341,24 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
*result = NULL;
*resultlen = 0;
/* We need to prepend the padding indicator. */
buf = xtrymalloc (datalen + 1);
if (!buf)
return out_of_core ();
*buf = 0; /* Padding indicator. */
memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
result, resultlen);
xfree (buf);
if (padind >= 0)
{
/* We need to prepend the padding indicator. */
buf = xtrymalloc (datalen + 1);
if (!buf)
return gpg_error (gpg_err_code_from_errno (errno));
*buf = padind; /* Padding indicator. */
memcpy (buf+1, data, datalen);
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen+1, buf,
result, resultlen);
xfree (buf);
}
else
{
sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86, datalen, data,
result, resultlen);
}
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
@ -381,3 +479,139 @@ iso7816_get_challenge (int slot, int length, unsigned char *buffer)
return 0;
}
/* Perform a READ BINARY command requesting a maximum of NMAX bytes
from OFFSET. With NMAX = 0 the entire file is read. The result is
stored in a newly allocated buffer at the address passed by RESULT.
Returns the length of this data at the address of RESULTLEN. */
gpg_error_t
iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
int read_all = !nmax;
size_t n;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (offset > 32767)
return gpg_error (GPG_ERR_INV_VALUE);
do
{
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver or the TCOS cards have problems
with an Le of 0. */
if (read_all || nmax > 254)
n = 254;
else
n = nmax;
sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
n, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
if (*result) /* Need to extend the buffer. */
{
unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
if (!p)
{
gpg_error_t err = gpg_error_from_errno (errno);
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return err;
}
*result = p;
memcpy (*result + *resultlen, buffer, bufferlen);
*resultlen += bufferlen;
xfree (buffer);
buffer = NULL;
}
else /* Transfer the buffer into our result. */
{
*result = buffer;
*resultlen = bufferlen;
}
offset += bufferlen;
if (offset > 32767)
break; /* We simply truncate the result for too large
files. */
if (nmax > bufferlen)
nmax -= bufferlen;
else
nmax = 0;
}
while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
return 0;
}
/* Perform a READ RECORD command. RECNO gives the record number to
read with 0 indicating the current record. RECCOUNT must be 1 (not
all cards support reading of more than one record). SHORT_EF
should be 0 to read the current EF or contain a short EF. The
result is stored in a newly allocated buffer at the address passed
by RESULT. Returns the length of this data at the address of
RESULTLEN. */
gpg_error_t
iso7816_read_record (int slot, int recno, int reccount, int short_ef,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buffer;
size_t bufferlen;
if (!result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
/* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
we check for this limit. */
if (recno < 0 || recno > 255 || reccount != 1
|| short_ef < 0 || short_ef > 254 )
return gpg_error (GPG_ERR_INV_VALUE);
buffer = NULL;
bufferlen = 0;
/* Fixme: Either the ccid driver of the TCOS cards have problems
with an Le of 0. */
sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
recno,
short_ef? short_ef : 0x04,
-1, NULL,
254, &buffer, &bufferlen);
if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
{
/* Make sure that pending buffers are released. */
xfree (buffer);
xfree (*result);
*result = NULL;
*resultlen = 0;
return map_sw (sw);
}
*result = buffer;
*resultlen = bufferlen;
return 0;
}

View File

@ -27,6 +27,10 @@
gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen);
gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_list_directory (int slot, int list_dirs,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_verify (int slot,
int chvno, const char *chv, size_t chvlen);
gpg_error_t iso7816_change_reference_data (int slot, int chvno,
@ -38,11 +42,15 @@ gpg_error_t iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen);
gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
const unsigned char *data,
size_t datalen);
gpg_error_t iso7816_compute_ds (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_decipher (int slot,
const unsigned char *data, size_t datalen,
int padind,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
@ -56,5 +64,10 @@ gpg_error_t iso7816_read_public_key (int slot,
gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer);
gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
int short_ef,
unsigned char **result, size_t *resultlen);
#endif /*ISO7816_H*/

View File

@ -195,6 +195,7 @@ struct
const char *ctapi_driver; /* Library to access the ctAPI. */
const char *pcsc_driver; /* Library to access the PC/SC system. */
int disable_ccid; /* Disable the use of the internal CCID driver. */
int allow_admin; /* Allow the use of Admin commands. */
#endif /*ENABLE_CARD_SUPPORT*/
} opt;

View File

@ -83,6 +83,7 @@ get_status_string ( int no )
case STATUS_ENTER : s = "ENTER"; break;
case STATUS_LEAVE : s = "LEAVE"; break;
case STATUS_ABORT : s = "ABORT"; break;
case STATUS_NEWSIG : s = "NEWSIG"; break;
case STATUS_GOODSIG: s = "GOODSIG"; break;
case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break;
case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;

View File

@ -101,6 +101,7 @@
#define STATUS_IMPORT_CHECK 69
#define STATUS_REVKEYSIG 70
#define STATUS_CARDCTRL 71
#define STATUS_NEWSIG 72
/*-- status.c --*/
void set_status_fd ( int fd );

209
g10/tlv.c Normal file
View File

@ -0,0 +1,209 @@
/* tlv.c - Tag-Length-Value Utilities
* Copyright (C) 2003, 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include "errors.h"
#include "util.h"
#include "packet.h"
#include "tlv.h"
static const unsigned char *
do_find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes, int nestlevel)
{
const unsigned char *s = buffer;
size_t n = length;
size_t len;
int this_tag;
int composite;
for (;;)
{
buffer = s;
if (n < 2)
return NULL; /* Buffer definitely too short for tag and length. */
if (!*s || *s == 0xff)
{ /* Skip optional filler between TLV objects. */
s++;
n--;
continue;
}
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
s++;
n--;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
if ((*s & 0x1f) == 0x1f)
return NULL; /* We support only up to 2 bytes. */
this_tag = (s[-1] << 8) | (s[0] & 0x7f);
}
else
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
if (len < 0x80)
;
else if (len == 0x81)
{ /* One byte length follows. */
if (!n)
return NULL; /* we expected 1 more bytes with the length. */
len = s[0];
s++; n--;
}
else if (len == 0x82)
{ /* Two byte length follows. */
if (n < 2)
return NULL; /* We expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
else
return NULL; /* APDU limit is 65535, thus it does not make
sense to assume longer length fields. */
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for a too deep
nesting. */
const unsigned char *tmp_s;
size_t tmp_len;
tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
if (tmp_s)
{
*nbytes = tmp_len;
return tmp_s;
}
}
if (this_tag == tag)
{
*nbytes = len;
return s;
}
if (len > n)
return NULL; /* Buffer too short to skip to the next tag. */
s += len; n -= len;
}
}
/* Locate a TLV encoded data object in BUFFER of LENGTH and
return a pointer to value as well as its length in NBYTES. Return
NULL if it was not found. Note, that the function does not check
whether the value fits into the provided buffer. */
const unsigned char *
find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes)
{
return do_find_tlv (buffer, length, tag, nbytes, 0);
}
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
gpg_error_t
parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed, int *r_ndef,
size_t *r_length, size_t *r_nhdr)
{
int c;
unsigned long tag;
const unsigned char *buf = *buffer;
size_t length = *size;
*r_ndef = 0;
*r_length = 0;
*r_nhdr = 0;
/* Get the tag. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
*r_class = (c & 0xc0) >> 6;
*r_constructed = !!(c & 0x20);
tag = c & 0x1f;
if (tag == 0x1f)
{
tag = 0;
do
{
tag <<= 7;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
tag |= c & 0x7f;
}
while (c & 0x80);
}
*r_tag = tag;
/* Get the length. */
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
if ( !(c & 0x80) )
*r_length = c;
else if (c == 0x80)
*r_ndef = 1;
else if (c == 0xff)
return gpg_error (GPG_ERR_BAD_BER);
else
{
unsigned long len = 0;
int count = c & 0x7f;
if (count > sizeof (len) || count > sizeof (size_t))
return gpg_error (GPG_ERR_BAD_BER);
for (; count; count--)
{
len <<= 8;
if (!length)
return gpg_error (GPG_ERR_EOF);
c = *buf++; length--; ++*r_nhdr;
len |= c & 0xff;
}
*r_length = len;
}
/* Without this kludge some example certs can't be parsed. */
if (*r_class == CLASS_UNIVERSAL && !*r_tag)
*r_length = 0;
*buffer = buf;
*size = length;
return 0;
}

86
g10/tlv.h Normal file
View File

@ -0,0 +1,86 @@
/* tlv.h - Tag-Length-Value Utilities
* Copyright (C) 2004 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
* GnuPG is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* GnuPG 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*/
#ifndef SCD_TLV_H
#define SCD_TLV_H 1
#include "types.h"
#include "cardglue.h"
enum tlv_tag_class {
CLASS_UNIVERSAL = 0,
CLASS_APPLICATION = 1,
CLASS_CONTEXT = 2,
CLASS_PRIVATE =3
};
enum tlv_tag_type {
TAG_NONE = 0,
TAG_BOOLEAN = 1,
TAG_INTEGER = 2,
TAG_BIT_STRING = 3,
TAG_OCTET_STRING = 4,
TAG_NULL = 5,
TAG_OBJECT_ID = 6,
TAG_OBJECT_DESCRIPTOR = 7,
TAG_EXTERNAL = 8,
TAG_REAL = 9,
TAG_ENUMERATED = 10,
TAG_EMBEDDED_PDV = 11,
TAG_UTF8_STRING = 12,
TAG_REALTIVE_OID = 13,
TAG_SEQUENCE = 16,
TAG_SET = 17,
TAG_NUMERIC_STRING = 18,
TAG_PRINTABLE_STRING = 19,
TAG_TELETEX_STRING = 20,
TAG_VIDEOTEX_STRING = 21,
TAG_IA5_STRING = 22,
TAG_UTC_TIME = 23,
TAG_GENERALIZED_TIME = 24,
TAG_GRAPHIC_STRING = 25,
TAG_VISIBLE_STRING = 26,
TAG_GENERAL_STRING = 27,
TAG_UNIVERSAL_STRING = 28,
TAG_CHARACTER_STRING = 29,
TAG_BMP_STRING = 30
};
/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
pointer to value as well as its length in NBYTES. Return NULL if
it was not found. Note, that the function does not check whether
the value fits into the provided buffer.*/
const unsigned char *find_tlv (const unsigned char *buffer, size_t length,
int tag, size_t *nbytes);
/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
and the length part from the TLV triplet. Update BUFFER and SIZE
on success. */
gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
int *r_class, int *r_tag,
int *r_constructed,
int *r_ndef, size_t *r_length, size_t *r_nhdr);
#endif /* SCD_TLV_H */