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:
parent
0c67c75cbe
commit
577d9c2342
@ -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
|
||||
|
@ -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 =
|
||||
|
717
g10/apdu.c
717
g10/apdu.c
@ -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
|
||||
|
16
g10/apdu.h
16
g10/apdu.h
@ -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,
|
||||
|
@ -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
|
||||
|
||||
|
||||
|
@ -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");
|
||||
|
@ -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));
|
||||
|
@ -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");
|
||||
|
@ -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,
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
|
272
g10/iso7816.c
272
g10/iso7816.c
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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*/
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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
209
g10/tlv.c
Normal 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
86
g10/tlv.h
Normal 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 */
|
Loading…
x
Reference in New Issue
Block a user