* scdaemon.h: Include gpg-error.h and errno.h

* card.c (map_sc_err): Use unknown for the error source.
* Makefile.am: Link with libgpg-error
This commit is contained in:
Werner Koch 2003-06-03 20:08:03 +00:00
parent c3cdaeeff7
commit fa0832884d
7 changed files with 1266 additions and 10 deletions

View File

@ -21,7 +21,7 @@
localedir = $(datadir)/locale
INCLUDES = -I../intl -DLOCALEDIR=\"$(localedir)\"
bin_PROGRAMS = scdaemon sc-investigate sc-genkey
bin_PROGRAMS = scdaemon sc-investigate
AM_CPPFLAGS = -I$(top_srcdir)/common $(OPENSC_CFLAGS) $(LIBGCRYPT_CFLAGS) \
$(KSBA_CFLAGS) $(LIBASSUAN_CFLAGS)
@ -48,15 +48,6 @@ sc_investigate_SOURCES = \
sc_investigate_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(LIBGCRYPT_LIBS) -lgpg-error -ldl
sc_genkey_SOURCES = \
sc-genkey.c scdaemon.h \
apdu.c apdu.h \
iso7816.c iso7816.h \
app-openpgp.c \
atr.c atr.h
sc_genkey_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
$(LIBGCRYPT_LIBS) -lgpg-error -ldl

442
scd/app-openpgp.c Normal file
View File

@ -0,0 +1,442 @@
/* app-openpgp.c - The OpenPGP card application.
* Copyright (C) 2003 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include "scdaemon.h"
#include "iso7816.h"
static struct {
int tag;
int constructed;
int get_from; /* Constructed DO with this DO or 0 for direct access. */
int binary;
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" },
{ 0 }
};
/* 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)
{
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. */
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 == 255)
{
if (n < 2)
return NULL; /* we expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
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)
{
*nbytes = len;
return s;
}
if (len > n)
return NULL; /* buffer too short to skip to the next tag. */
s += len; n -= len;
}
}
static void
dump_one_do (int slot, int tag)
{
int rc, i;
unsigned char *buffer;
size_t buflen;
const char *desc;
int binary;
const unsigned char *value;
size_t valuelen;
for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
;
desc = data_objects[i].tag? data_objects[i].desc : "?";
binary = data_objects[i].tag? data_objects[i].binary : 1;
value = NULL;
rc = -1;
if (data_objects[i].tag && data_objects[i].get_from)
{
rc = iso7816_get_data (slot, data_objects[i].get_from,
&buffer, &buflen);
if (!rc)
{
value = find_tlv (buffer, buflen, tag, &valuelen, 0);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
{
log_error ("warning: constructed DO too short\n");
value = NULL;
xfree (buffer); buffer = NULL;
}
}
}
if (!value) /* Not in a constructed DO, try simple. */
{
rc = iso7816_get_data (slot, tag, &buffer, &buflen);
if (!rc)
{
value = buffer;
valuelen = buflen;
}
}
if (rc == 0x6a88)
log_info ("DO `%s' not available\n", desc);
else if (rc)
log_info ("DO `%s' not available (rc=%04X)\n", desc, rc);
else
{
if (binary)
{
log_info ("DO `%s': ", desc);
log_printhex ("", value, valuelen);
}
else
log_info ("DO `%s': `%.*s'\n",
desc, (int)valuelen, value); /* FIXME: sanitize */
xfree (buffer);
}
}
static void
dump_all_do (int slot)
{
int rc, i, j;
unsigned char *buffer;
size_t buflen;
for (i=0; data_objects[i].tag; i++)
{
if (data_objects[i].get_from)
continue;
rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
if (rc == 0x6a88)
;
else if (rc)
log_info ("DO `%s' not available (rc=%04X)\n",
data_objects[i].desc, rc);
else
{
if (data_objects[i].binary)
{
log_info ("DO `%s': ", data_objects[i].desc);
log_printhex ("", buffer, buflen);
}
else
log_info ("DO `%s': `%.*s'\n",
data_objects[i].desc,
(int)buflen, buffer); /* FIXME: sanitize */
}
if (data_objects[i].constructed)
{
for (j=0; data_objects[j].tag; j++)
{
const unsigned char *value;
size_t valuelen;
if (j==i || data_objects[i].tag != data_objects[j].get_from)
continue;
value = find_tlv (buffer, buflen,
data_objects[j].tag, &valuelen, 0);
if (!value)
; /* not found */
else if (valuelen > buflen - (value - buffer))
log_error ("warning: constructed DO too short\n");
else
{
if (data_objects[j].binary)
{
log_info ("DO `%s': ", data_objects[j].desc);
log_printhex ("", value, valuelen);
}
else
log_info ("DO `%s': `%.*s'\n",
data_objects[j].desc,
(int)valuelen, value); /* FIXME: sanitize */
}
}
}
xfree (buffer); buffer = NULL;
}
}
static int
store_fpr (int slot, int keynumber, u32 timestamp,
const unsigned char *m, size_t mlen,
const unsigned char *e, size_t elen)
{
unsigned int n;
unsigned char *buffer, *p;
unsigned char fpr[20];
int rc;
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
return out_of_core ();
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
*p++ = n;
*p++ = 4; /* key packet version */
*p++ = timestamp >> 24;
*p++ = timestamp >> 16;
*p++ = timestamp >> 8;
*p++ = timestamp;
*p++ = 1; /* RSA */
*p++ = mlen >> 8;
*p++ = mlen;
memcpy (p, m, mlen); p += mlen;
*p++ = elen >> 8;
*p++ = elen;
memcpy (p, e, elen); p += elen;
gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
xfree (buffer);
rc = iso7816_put_data (slot, 0xC6 + keynumber, fpr, 20);
if (rc)
log_error ("failed to store the fingerprint: rc=%04X\n", rc);
return rc;
}
/* Generate a new key on the card and store the fingerprint in the
corresponding DO. A KEYNUMBER of 0 creates the digital signature
key, 1 the encryption key and 2 the authentication key. If the key
already exists an error is returned unless FORCE has been set to
true. Note, that the function does not return the public key; this
has to be done using openpgp_readkey(). */
int
openpgp_genkey (int slot, int keynumber, int force)
{
int rc;
int i;
const unsigned char *fpr;
const unsigned char *keydata, *m, *e;
unsigned char *buffer;
size_t buflen, keydatalen, n, mlen, elen;
time_t created_at;
if (keynumber < 0 || keynumber > 2)
return -1; /* invalid value */
rc = iso7816_get_data (slot, 0x006E, &buffer, &buflen);
if (rc)
{
log_error ("error reading application data\n");
return -1;
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
if (!fpr || n != 60)
{
log_error ("error reading fingerprint DO\n");
goto leave;
}
fpr += 20*keynumber;
for (i=0; i < 20 && !fpr[i]; i++)
;
if (i!=20 && !force)
{
log_error ("key already exists\n");
goto leave;
}
else if (i!=20)
log_info ("existing key will be replaced\n");
else
log_info ("generating new key\n");
rc = iso7816_verify (slot, 0x83, "12345678", 8);
if (rc)
{
log_error ("verify CHV3 failed: rc=%04X\n", rc);
goto leave;
}
xfree (buffer); buffer = NULL;
rc = iso7816_generate_keypair (slot,
keynumber == 0? "\xB6" :
keynumber == 1? "\xB8" : "\xA4",
2,
&buffer, &buflen);
if (rc)
{
log_error ("generating key failed\n");
goto leave;
}
keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen, 0);
if (!keydata)
{
log_error ("response does not contain the public key data\n");
goto leave;
}
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
if (!m)
{
log_error ("response does not contain the RSA modulus\n");
goto leave;
}
log_printhex ("RSA n:", m, mlen);
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
if (!e)
{
log_error ("response does not contain the RSA public exponent\n");
goto leave;
}
log_printhex ("RSA e:", e, elen);
created_at = gnupg_get_time ();
rc = store_fpr (slot, keynumber, (u32)created_at, m, mlen, e, elen);
leave:
xfree (buffer);
return rc;
}
/* Select the OpenPGP application on the card in SLOT. This function
must be used to before any other OpenPGP application functions. */
int
app_select_openpgp (int slot)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
int rc;
unsigned char *buffer;
size_t buflen;
rc = iso7816_select_application (slot, aid, sizeof aid);
if (!rc)
{
/* fixme: get the full AID and check that the version is okay
with us. */
rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
if (rc)
goto leave;
if (opt.verbose)
log_info ("got AID: ");
log_printhex ("", buffer, buflen);
xfree (buffer);
dump_all_do (slot);
/* rc = iso7816_verify (slot, 0x83, "12345678", 8); */
/* if (rc) */
/* log_error ("verify CHV3 failed: rc=%04X\n", rc); */
/* rc = iso7816_put_data (slot, 0x005B, "Joe Hacker", 10); */
/* if (rc) */
/* log_error ("failed to set `Name': rc=%04X\n", rc); */
/* else */
/* dump_one_do (slot, 0x005B); */
/* fixme: associate the internal state with the slot */
}
leave:
return rc;
}

287
scd/atr.c Normal file
View File

@ -0,0 +1,287 @@
/* atr.c - ISO 7816 ATR fucntions
* Copyright (C) 2003 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include <assert.h>
#include "scdaemon.h"
#include "apdu.h"
#include "atr.h"
static int const fi_table[16] = { 0, 372, 558, 744, 1116,1488, 1860, -1,
-1, 512, 768, 1024, 1536, 2048, -1, -1 };
static int const di_table[16] = { -1, 1, 2, 4, 8, 16, -1, -1,
0, -1, -2, -4, -8, -16, -32, -64};
/* Dump the ATR of the card at SLOT in a human readable format to
stream FP. */
int
atr_dump (int slot, FILE *fp)
{
unsigned char *atrbuffer, *atr;
size_t atrlen;
int have_ta, have_tb, have_tc, have_td;
int n_historical;
int idx, val;
unsigned char chksum;
atr = atrbuffer = apdu_get_atr (slot, &atrlen);
if (!atr)
return gpg_error (GPG_ERR_GENERAL);
fprintf (fp, "Info on ATR of length %u at slot %d\n",
(unsigned int)atrlen, slot);
if (!atrlen)
{
fprintf (fp, "error: empty ATR\n");
goto bailout;
}
if (*atr == 0x3b)
fputs ("direct convention\n", fp);
else if (*atr == 0x3f)
fputs ("inverse convention\n", fp);
else
fprintf (fp,"error: invalid TS character 0x%02x\n", *atr);
if (!--atrlen)
goto bailout;
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);
fprintf (fp, "%d historical characters indicated\n", n_historical);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp);
if (!--atrlen)
goto bailout;
atr++;
if (have_ta)
{
fputs ("TA1: F=", fp);
val = fi_table[(*atr >> 4) & 0x0f];
if (!val)
fputs ("internal clock", fp);
else if (val == -1)
fputs ("RFU", fp);
else
fprintf (fp, "%d", val);
fputs (" D=", fp);
val = di_table[*atr & 0x0f];
if (!val)
fputs ("[impossible value]\n", fp);
else if (val == -1)
fputs ("RFU\n", fp);
else if (val < 0 )
fprintf (fp, "1/%d\n", val);
else
fprintf (fp, "%d\n", val);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tb)
{
fprintf (fp, "TB1: II=%d PI1=%d%s\n", (*atr >> 5) & 3, *atr & 0x1f,
(*atr & 0x80)? " [high bit not cleared]":"");
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tc)
{
if (*atr == 255)
fputs ("TC1: guard time shortened to 1 etu\n", fp);
else
fprintf (fp, "TC1: (extra guard time) N=%d\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
fprintf (fp, "TD1: protocol T%d supported\n", *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp);
if (!--atrlen)
goto bailout;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
if (have_ta)
{
fprintf (fp, "TA2: (PTS) %stoggle, %splicit, T=%02X\n",
(*atr & 0x80)? "no-":"",
(*atr & 0x10)? "im": "ex",
(*atr & 0x0f));
if ((*atr & 0x60))
fprintf (fp, "note: reserved bits are set (TA2=0x%02X)\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tb)
{
fprintf (fp, "TB2: PI2=%d\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tc)
{
fprintf (fp, "TC2: PWI=%d\n", *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
fprintf (fp, "TD2: protocol T%d supported\n", *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n", fp);
if (!--atrlen)
goto bailout;
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)
{
fprintf (fp, "TA%d: IFSC=%d\n", idx, *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tb)
{
fprintf (fp, "TB%d: BWI=%d CWI=%d\n",
idx, (*atr >> 4) & 0x0f, *atr & 0x0f);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_tc)
{
fprintf (fp, "TC%d: 0x%02X\n", idx, *atr);
if (!--atrlen)
goto bailout;
atr++;
}
if (have_td)
{
have_ta = !!(*atr & 0x10);
have_tb = !!(*atr & 0x20);
have_tc = !!(*atr & 0x40);
have_td = !!(*atr & 0x80);
fprintf (fp, "TD%d: protocol T%d supported\n", idx, *atr & 0x0f);
if (have_ta + have_tb + have_tc + have_td + n_historical > atrlen)
fputs ("error: ATR shorter than indicated by format character\n",
fp);
if (!--atrlen)
goto bailout;
atr++;
}
else
have_ta = have_tb = have_tc = have_td = 0;
}
if (n_historical + 1 > atrlen)
fputs ("error: ATR shorter than required for historical bytes "
"and checksum\n", fp);
if (n_historical)
{
fputs ("Historical:", fp);
for (; n_historical && atrlen ; n_historical--, atrlen--, atr++)
fprintf (fp, " %02X", *atr);
putchar ('\n');
}
if (!atrlen)
fputs ("error: checksum missing\n", fp);
else if (*atr == chksum)
fprintf (fp, "TCK: %02X (good)\n", *atr);
else
fprintf (fp, "TCK: %02X (bad; calculated %02X)\n", *atr, chksum);
atrlen--;
if (atrlen)
fprintf (fp, "error: %u bytes garbage at end of ATR\n",
(unsigned int)atrlen );
bailout:
xfree (atrbuffer);
return 0;
}

28
scd/atr.h Normal file
View File

@ -0,0 +1,28 @@
/* atr.h - ISO 7816 ATR functions
* Copyright (C) 2003 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 ATR_H
#define ATR_H
int atr_dump (int slot, FILE *fp);
#endif /*ATR_H*/

299
scd/iso7816.c Normal file
View File

@ -0,0 +1,299 @@
/* iso7816.c - ISO 7816 commands
* Copyright (C) 2003 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 <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dlfcn.h>
#include "scdaemon.h"
#include "iso7816.h"
#include "apdu.h"
#define CMD_SELECT_FILE 0xA4
#define CMD_VERIFY 0x20
#define CMD_GET_DATA 0xCA
#define CMD_PUT_DATA 0xDA
#define CMD_PSO 0x2A
#define CMD_INTERNAL_AUTHENTICATE 0x88
#define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84
/* This function is specialized version of the SELECT FILE command.
SLOT is the card and reader as created for example by
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
internally mapped. */
int
iso7816_select_application (int slot, const char *aid, size_t aidlen)
{
int sw;
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
}
/* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
int
iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
{
int sw;
sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
}
/* Perform a GET DATA command requesting TAG and storing the result in
a newly allocated buffer at the address passed by RESULT. Return
the length of this data at the address of RESULTLEN. */
int
iso7816_get_data (int slot, int tag,
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, 0x00, CMD_GET_DATA,
((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
}
return 0;
}
/* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */
int
iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen)
{
int sw;
sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
((tag >> 8) & 0xff), (tag & 0xff),
datalen, data);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
}
/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
success 0 is returned and the data is availavle in a newly
allocated buffer stored at RESULT with its length stored at
RESULTLEN. */
int
iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!data || !datalen || !result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, data,
result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
}
return 0;
}
/* 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. */
int
iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
int sw;
unsigned char *buf;
if (!data || !datalen || !result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*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 (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
}
return 0;
}
int
iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!data || !datalen || !result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
datalen, data, result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
}
return 0;
}
static int
generate_keypair (int slot, int readonly,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
int sw;
if (!data || !datalen || !result || !resultlen)
return gpg_error (GPG_ERR_INV_VALUE);
*result = NULL;
*resultlen = 0;
sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
datalen, data, result, resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
}
return 0;
}
int
iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
return generate_keypair (slot, 0, data, datalen, result, resultlen);
}
int
iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
return generate_keypair (slot, 1, data, datalen, result, resultlen);
}
int
iso1816_get_challenge (int slot, int length, unsigned char *buffer)
{
int sw;
unsigned char *result;
size_t resultlen;
if (!buffer || length < 1)
return gpg_error (GPG_ERR_INV_VALUE);
do
{
result = NULL;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
length,
&result, &resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
return -1; /* FIXME: Map error codes. */
}
if (resultlen > length)
resultlen = length;
memcpy (buffer, result, resultlen);
buffer += resultlen;
length -= length;
xfree (result);
}
while (length > 0);
return 0;
}

48
scd/iso7816.h Normal file
View File

@ -0,0 +1,48 @@
/* iso7816.h - ISO 7816 commands
* Copyright (C) 2003 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 ISO7816_H
#define ISO7816_H
int iso7816_select_application (int slot, const char *aid, size_t aidlen);
int iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen);
int iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen);
int iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen);
int iso7816_compute_ds (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_decipher (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso1816_get_challenge (int slot, int length, unsigned char *buffer);
#endif /*ISO7816_H*/

161
scd/sc-investigate.c Normal file
View File

@ -0,0 +1,161 @@
/* sc-investigate.c - A tool to look around on smartcards.
* Copyright (C) 2003 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>
#define JNLIB_NEED_LOG_LOGV
#include <gcrypt.h>
#include "scdaemon.h"
#include "apdu.h" /* for open_reader */
#include "atr.h"
#define _(a) (a)
enum cmd_and_opt_values
{ oVerbose = 'v',
oReaderPort = 500,
oDebug,
oDebugAll,
aTest };
static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, "@Options:\n " },
{ oVerbose, "verbose", 0, "verbose" },
{ oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
{ oDebug, "debug" ,4|16, "set debugging flags"},
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
{0}
};
static const char *
my_strusage (int level)
{
const char *p;
switch (level)
{
case 11: p = "sc-investigate (GnuPG)";
break;
case 13: p = VERSION; break;
case 17: p = PRINTABLE_OS_NAME; break;
case 19: p = _("Please report bugs to <" PACKAGE_BUGREPORT ">.\n");
break;
case 1:
case 40: p = _("Usage: sc-investigate [options] (-h for help)\n");
break;
case 41: p = _("Syntax: sc-investigate [options] [args]]\n"
"Have a look at smartcards\n");
break;
default: p = NULL;
}
return p;
}
/* Used by gcry for logging */
static void
my_gcry_logger (void *dummy, int level, const char *fmt, va_list arg_ptr)
{
/* translate the log levels */
switch (level)
{
case GCRY_LOG_CONT: level = JNLIB_LOG_CONT; break;
case GCRY_LOG_INFO: level = JNLIB_LOG_INFO; break;
case GCRY_LOG_WARN: level = JNLIB_LOG_WARN; break;
case GCRY_LOG_ERROR:level = JNLIB_LOG_ERROR; break;
case GCRY_LOG_FATAL:level = JNLIB_LOG_FATAL; break;
case GCRY_LOG_BUG: level = JNLIB_LOG_BUG; break;
case GCRY_LOG_DEBUG:level = JNLIB_LOG_DEBUG; break;
default: level = JNLIB_LOG_ERROR; break;
}
log_logv (level, fmt, arg_ptr);
}
int
main (int argc, char **argv )
{
ARGPARSE_ARGS pargs;
int slot, rc;
int reader_port = 32768; /* First USB reader. */
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
log_set_prefix ("sc-investigate", 1);
/* check that the libraries are suitable. Do it here because
the option parsing may need services of the library */
if (!gcry_check_version (NEED_LIBGCRYPT_VERSION) )
{
log_fatal( _("libgcrypt is too old (need %s, have %s)\n"),
NEED_LIBGCRYPT_VERSION, gcry_check_version (NULL) );
}
gcry_set_log_handler (my_gcry_logger, NULL);
/* FIXME? gcry_control (GCRYCTL_USE_SECURE_RNDPOOL);*/
pargs.argc = &argc;
pargs.argv = &argv;
pargs.flags= 1; /* do not remove the args */
while (arg_parse (&pargs, opts) )
{
switch (pargs.r_opt)
{
case oVerbose: opt.verbose++; break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
default : pargs.err = 2; break;
}
}
if (log_get_errorcount(0))
exit(2);
if (argc)
usage (1);
slot = apdu_open_reader (reader_port);
if (slot == -1)
exit (1);
rc = atr_dump (slot, stdout);
if (rc)
log_error ("can't dump ATR: %s\n", gnupg_strerror (rc));
rc = app_select_openpgp (slot);
if (rc)
log_error ("selecting openpgp failed: %s\n", gnupg_strerror (rc));
else
log_info ("openpgp application selected\n");
return 0;
}