1
0
mirror of git://git.gnupg.org/gnupg.git synced 2025-01-18 14:17:03 +01:00

scd: Add option --dump-atr to command APDU.

* scd/atr.c: Rewrite.
* scd/Makefile.am (scdaemon_SOURCES): Add atr.c and atr.h.
* scd/command.c (cmd_apdu): Add option --dump-atr.
This commit is contained in:
Werner Koch 2011-12-15 14:47:04 +01:00
parent 7737a2c269
commit b22d62bd14
4 changed files with 107 additions and 184 deletions

View File

@ -37,6 +37,7 @@ card_apps = app-openpgp.c app-nks.c app-dinsig.c app-p15.c app-geldkarte.c
scdaemon_SOURCES = \ scdaemon_SOURCES = \
scdaemon.c scdaemon.h \ scdaemon.c scdaemon.h \
command.c \ command.c \
atr.c atr.h \
apdu.c apdu.h \ apdu.c apdu.h \
ccid-driver.c ccid-driver.h \ ccid-driver.c ccid-driver.h \
iso7816.c iso7816.h \ iso7816.c iso7816.h \

238
scd/atr.c
View File

@ -1,5 +1,5 @@
/* atr.c - ISO 7816 ATR fucntions /* atr.c - ISO 7816 ATR fucntions
* Copyright (C) 2003 Free Software Foundation, Inc. * Copyright (C) 2003, 2011 Free Software Foundation, Inc.
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -24,10 +24,9 @@
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
#include "scdaemon.h" #include "../common/estream.h"
#include "apdu.h" #include "../common/logging.h"
#include "atr.h" #include "atr.h"
#include "dynload.h"
static int const fi_table[16] = { 0, 372, 558, 744, 1116,1488, 1860, -1, static int const fi_table[16] = { 0, 372, 558, 744, 1116,1488, 1860, -1,
-1, 512, 768, 1024, 1536, 2048, -1, -1 }; -1, 512, 768, 1024, 1536, 2048, -1, -1 };
@ -35,37 +34,42 @@ static int const di_table[16] = { -1, 1, 2, 4, 8, 16, -1, -1,
0, -1, -2, -4, -8, -16, -32, -64}; 0, -1, -2, -4, -8, -16, -32, -64};
/* Dump the ATR of the card at SLOT in a human readable format to /* Dump the ATR in (BUFFER,BUFLEN) to a human readable format and
stream FP. */ return that as a malloced buffer. The caller must release this
int buffer using es_free! On error this function returns NULL and sets
atr_dump (int slot, FILE *fp) ERRNO. */
char *
atr_dump (const void *buffer, size_t buflen)
{ {
unsigned char *atrbuffer, *atr; const unsigned char *atr = buffer;
size_t atrlen; size_t atrlen = buflen;
estream_t fp;
int have_ta, have_tb, have_tc, have_td; int have_ta, have_tb, have_tc, have_td;
int n_historical; int n_historical;
int idx, val; int idx, val;
unsigned char chksum; unsigned char chksum;
char *result;
atr = atrbuffer = apdu_get_atr (slot, &atrlen); fp = es_fopenmem (0, "rwb");
if (!atr) if (!fp)
return gpg_error (GPG_ERR_GENERAL); return NULL;
fprintf (fp, "Info on ATR of length %u at slot %d\n",
(unsigned int)atrlen, slot);
if (!atrlen) if (!atrlen)
{ {
fprintf (fp, "error: empty ATR\n"); es_fprintf (fp, "error: empty ATR\n");
goto bailout; goto bailout;
} }
for (idx=0; idx < atrlen ; idx++)
es_fprintf (fp, "%s%02X", idx?" ":"", atr[idx]);
es_putc ('\n', fp);
if (*atr == 0x3b) if (*atr == 0x3b)
fputs ("direct convention\n", fp); es_fputs ("Direct convention\n", fp);
else if (*atr == 0x3f) else if (*atr == 0x3f)
fputs ("inverse convention\n", fp); es_fputs ("Inverse convention\n", fp);
else else
fprintf (fp,"error: invalid TS character 0x%02x\n", *atr); es_fprintf (fp,"error: invalid TS character 0x%02x\n", *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -79,34 +83,34 @@ atr_dump (int slot, FILE *fp)
have_tc = !!(*atr & 0x40); have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80); have_td = !!(*atr & 0x80);
n_historical = (*atr & 0x0f); n_historical = (*atr & 0x0f);
fprintf (fp, "%d historical characters indicated\n", n_historical); es_fprintf (fp, "%d historical characters indicated\n", n_historical);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen) if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp); es_fputs ("error: ATR shorter than indicated by format character\n", fp);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
if (have_ta) if (have_ta)
{ {
fputs ("TA1: F=", fp); es_fputs ("TA1: F=", fp);
val = fi_table[(*atr >> 4) & 0x0f]; val = fi_table[(*atr >> 4) & 0x0f];
if (!val) if (!val)
fputs ("internal clock", fp); es_fputs ("internal clock", fp);
else if (val == -1) else if (val == -1)
fputs ("RFU", fp); es_fputs ("RFU", fp);
else else
fprintf (fp, "%d", val); es_fprintf (fp, "%d", val);
fputs (" D=", fp); es_fputs (" D=", fp);
val = di_table[*atr & 0x0f]; val = di_table[*atr & 0x0f];
if (!val) if (!val)
fputs ("[impossible value]\n", fp); es_fputs ("[impossible value]\n", fp);
else if (val == -1) else if (val == -1)
fputs ("RFU\n", fp); es_fputs ("RFU\n", fp);
else if (val < 0 ) else if (val < 0 )
fprintf (fp, "1/%d\n", val); es_fprintf (fp, "1/%d\n", val);
else else
fprintf (fp, "%d\n", val); es_fprintf (fp, "%d\n", val);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -115,7 +119,8 @@ atr_dump (int slot, FILE *fp)
if (have_tb) if (have_tb)
{ {
fprintf (fp, "TB1: II=%d PI1=%d%s\n", (*atr >> 5) & 3, *atr & 0x1f, es_fprintf (fp, "TB1: II=%d PI1=%d%s\n",
((*atr >> 5) & 3), (*atr & 0x1f),
(*atr & 0x80)? " [high bit not cleared]":""); (*atr & 0x80)? " [high bit not cleared]":"");
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -125,9 +130,9 @@ atr_dump (int slot, FILE *fp)
if (have_tc) if (have_tc)
{ {
if (*atr == 255) if (*atr == 255)
fputs ("TC1: guard time shortened to 1 etu\n", fp); es_fputs ("TC1: guard time shortened to 1 etu\n", fp);
else else
fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr); es_fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -140,10 +145,11 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20); have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40); have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80); have_td = !!(*atr & 0x80);
fprintf (fp, "TD1: protocol T%d supported\n", *atr & 0x0f); es_fprintf (fp, "TD1: protocol T%d supported\n", (*atr & 0x0f));
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen) if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp); es_fputs ("error: ATR shorter than indicated by format character\n",
fp);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -154,12 +160,12 @@ atr_dump (int slot, FILE *fp)
if (have_ta) if (have_ta)
{ {
fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n", es_fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n",
(*atr & 0x80)? "no-":"", (*atr & 0x80)? "no-":"",
(*atr & 0x10)? "im": "ex", (*atr & 0x10)? "im": "ex",
(*atr & 0x0f)); (*atr & 0x0f));
if ((*atr & 0x60)) if ((*atr & 0x60))
fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr); es_fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -167,7 +173,7 @@ atr_dump (int slot, FILE *fp)
if (have_tb) if (have_tb)
{ {
fprintf (fp, "TB2: PI2=%d\n", *atr); es_fprintf (fp, "TB2: PI2=%d\n", *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -175,7 +181,7 @@ atr_dump (int slot, FILE *fp)
if (have_tc) if (have_tc)
{ {
fprintf (fp, "TC2: PWI=%d\n", *atr); es_fprintf (fp, "TC2: PWI=%d\n", *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -187,10 +193,11 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20); have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40); have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80); have_td = !!(*atr & 0x80);
fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f); es_fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen) if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp); es_fputs ("error: ATR shorter than indicated by format character\n",
fp);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -203,7 +210,7 @@ atr_dump (int slot, FILE *fp)
{ {
if (have_ta) if (have_ta)
{ {
fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr); es_fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -211,7 +218,7 @@ atr_dump (int slot, FILE *fp)
if (have_tb) if (have_tb)
{ {
fprintf (fp, "TB%d: BWI=%d CWI=%d\n", es_fprintf (fp, "TB%d: BWI=%d CWI=%d\n",
idx, (*atr >> 4) & 0x0f, *atr & 0x0f); idx, (*atr >> 4) & 0x0f, *atr & 0x0f);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
@ -220,7 +227,7 @@ atr_dump (int slot, FILE *fp)
if (have_tc) if (have_tc)
{ {
fprintf (fp, "TC%d: 0x%02X\n", idx, *atr); es_fprintf (fp, "TC%d: 0x%02X\n", idx, *atr);
if (!--atrlen) if (!--atrlen)
goto bailout; goto bailout;
atr++; atr++;
@ -232,10 +239,11 @@ atr_dump (int slot, FILE *fp)
have_tb = !!(*atr & 0x20); have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40); have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80); have_td = !!(*atr & 0x80);
fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f); es_fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen) if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", es_fputs ("error: "
"ATR shorter than indicated by format character\n",
fp); fp);
if (!--atrlen) if (!--atrlen)
@ -247,150 +255,36 @@ atr_dump (int slot, FILE *fp)
} }
if (n_historical + 1 > atrlen) if (n_historical + 1 > atrlen)
fputs ("error: ATR shorter than required for historical bytes " es_fputs ("error: ATR shorter than required for historical bytes "
"and checksum\n", fp); "and checksum\n", fp);
if (n_historical) if (n_historical)
{ {
fputs ("Historical:", fp); es_fputs ("HCH:", fp);
for (; n_historical && atrlen ; n_historical--, atrlen--, atr++) for (; n_historical && atrlen ; n_historical--, atrlen--, atr++)
fprintf (fp, " %02X", *atr); es_fprintf (fp, " %02X", *atr);
putchar ('\n'); es_putc ('\n', fp);
} }
if (!atrlen) if (!atrlen)
fputs ("error: checksum missing\n", fp); es_fputs ("error: checksum missing\n", fp);
else if (*atr == chksum) else if (*atr == chksum)
fprintf (fp, "TCK: %02X (good)\n", *atr); es_fprintf (fp, "TCK: %02X (good)\n", *atr);
else else
fprintf (fp, "TCK: %02X (bad; calculated %02X)\n", *atr, chksum); es_fprintf (fp, "TCK: %02X (bad; computed %02X)\n", *atr, chksum);
atrlen--; atrlen--;
if (atrlen) if (atrlen)
fprintf (fp, "error: %u bytes garbage at end of ATR\n", es_fprintf (fp, "error: %u bytes garbage at end of ATR\n",
(unsigned int)atrlen ); (unsigned int)atrlen );
bailout: bailout:
xfree (atrbuffer); es_putc ('\0', fp); /* We want a string. */
if (es_fclose_snatch (fp, (void**)&result, NULL))
return 0;
}
/* Note: This code has not yet been tested! It shall return -1 on
error or the number of historical bytes and store them at
HISTORICAL. */
int
atr_get_historical (int slot, unsigned char historical[])
{
int result = -1;
unsigned char *atrbuffer = NULL;
unsigned char *atr;
size_t atrlen;
int have_ta, have_tb, have_tc, have_td;
int n_historical;
int idx;
unsigned char chksum;
atr = atrbuffer = apdu_get_atr (slot, &atrlen);
if (!atr || atrlen < 2)
goto leave;
atrlen--;
atr++;
chksum = *atr;
for (idx=1; idx < atrlen-1; idx++)
chksum ^= atr[idx];
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
n_historical = (*atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{ {
have_ta = !!(*atr & 0x10); log_error ("oops: es_fclose_snatch failed: %s\n", strerror (errno));
have_tb = !!(*atr & 0x20); return NULL;
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
} }
else
have_ta = have_tb = have_tc = have_td = 0;
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++)
{
if (have_ta + have_tb + have_tc >= atrlen)
goto leave;
atrlen -= have_ta + have_tb + have_tc;
atr += have_ta + have_tb + have_tc;
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen)
goto leave; /* ATR shorter than indicated by format character. */
atrlen--;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
}
if (n_historical >= atrlen)
goto leave; /* ATR shorter than required for historical bytes. */
if (n_historical)
{
for (idx=0; n_historical && atrlen; n_historical--, atrlen--, atr++)
historical[idx] = *atr;
}
if (!atrlen || *atr != chksum)
goto leave;
/* Don't care about garbage at the end of the ATR. */
result = n_historical;
leave:
xfree (atrbuffer);
return result; return result;
} }

View File

@ -20,7 +20,7 @@
#ifndef ATR_H #ifndef ATR_H
#define ATR_H #define ATR_H
int atr_dump (int slot, FILE *fp); char *atr_dump (const void *buffer, size_t buflen);

View File

@ -35,11 +35,13 @@
#include <ksba.h> #include <ksba.h>
#include "app-common.h" #include "app-common.h"
#include "apdu.h" /* Required for apdu_*_reader (). */ #include "apdu.h" /* Required for apdu_*_reader (). */
#include "atr.h"
#include "exechelp.h" #include "exechelp.h"
#ifdef HAVE_LIBUSB #ifdef HAVE_LIBUSB
#include "ccid-driver.h" #include "ccid-driver.h"
#endif #endif
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */ /* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100 #define MAXLEN_PIN 100
@ -1795,7 +1797,7 @@ cmd_disconnect (assuan_context_t ctx, char *line)
static const char hlp_apdu[] = static const char hlp_apdu[] =
"APDU [--atr] [--more] [--exlen[=N]] [hexstring]\n" "APDU [--[dump-]atr] [--more] [--exlen[=N]] [hexstring]\n"
"\n" "\n"
"Send an APDU to the current reader. This command bypasses the high\n" "Send an APDU to the current reader. This command bypasses the high\n"
"level functions and sends the data directly to the card. HEXSTRING\n" "level functions and sends the data directly to the card. HEXSTRING\n"
@ -1826,6 +1828,9 @@ cmd_apdu (assuan_context_t ctx, char *line)
size_t exlen; size_t exlen;
int slot; int slot;
if (has_option (line, "--dump-atr"))
with_atr = 2;
else
with_atr = has_option (line, "--atr"); with_atr = has_option (line, "--atr");
handle_more = has_option (line, "--more"); handle_more = has_option (line, "--more");
@ -1861,10 +1866,33 @@ cmd_apdu (assuan_context_t ctx, char *line)
rc = gpg_error (GPG_ERR_INV_CARD); rc = gpg_error (GPG_ERR_INV_CARD);
goto leave; goto leave;
} }
if (with_atr == 2)
{
char *string, *p, *pend;
string = atr_dump (atr, atrlen);
if (string)
{
for (rc=0, p=string; !rc && (pend = strchr (p, '\n')); p = pend+1)
{
rc = assuan_send_data (ctx, p, pend - p + 1);
if (!rc)
rc = assuan_send_data (ctx, NULL, 0);
}
if (!rc && *p)
rc = assuan_send_data (ctx, p, strlen (p));
es_free (string);
if (rc)
goto leave;
}
}
else
{
bin2hex (atr, atrlen, hexbuf); bin2hex (atr, atrlen, hexbuf);
xfree (atr);
send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0); send_status_info (ctrl, "CARD-ATR", hexbuf, strlen (hexbuf), NULL, 0);
} }
xfree (atr);
}
apdu = hex_to_buffer (line, &apdulen); apdu = hex_to_buffer (line, &apdulen);
if (!apdu) if (!apdu)