* g10.c (main): New commands --card-edit, --card-status and

--change-pin.  New options --ctapi-driver, --pcsc-driver and
--disable-ccid
* options.h (DBG_CARD_IO): New.
* cardglue.c, cardclue.h: Enhanced.
* card-util.c: New. Taken from current the gnupg 1.9 branch.
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
* apdu.h, ccid-driver.c, ccid-driver.h: New.  Takem from the current
gnupg 1.9 branch withy minor changes to include directives.
* Makefile.am: Added these files.
This commit is contained in:
Werner Koch 2003-09-28 13:41:58 +00:00
parent 20ed53942a
commit e369270a65
15 changed files with 5576 additions and 2 deletions

View File

@ -1,10 +1,22 @@
2003-09-28 Werner Koch <wk@gnupg.org>
* g10.c (main): New commands --card-edit, --card-status and
--change-pin. New options --ctapi-driver, --pcsc-driver and
--disable-ccid
* options.h (DBG_CARD_IO): New.
* cardglue.c, cardclue.h: Enhanced.
* card-util.c: New. Taken from current the gnupg 1.9 branch.
* app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
* apdu.h, ccid-driver.c, ccid-driver.h: New. Takem from the current
gnupg 1.9 branch withy minor changes to include directives.
* Makefile.am: Added these files.
2003-09-27 Werner Koch <wk@gnupg.org>
* sign.c (do_sign) [ENABLE_CARD_SUPPORT]: Divert to card.
* cardglue.c, cardglue.h: New.
* Makefile.am (gpg_LDADD): Added.
(card_support_sources): New.
2003-09-25 David Shaw <dshaw@jabberwocky.com>

View File

@ -64,7 +64,13 @@ common_source = \
signal.c
card_support_source = \
cardglue.c cardclue.h
cardglue.c cardclue.h \
card-util.c \
app-common.h \
app-openpgp.c \
iso7816.c iso7816.h \
apdu.c apdu.h \
ccid-driver.c ccid-driver.h
gpg_SOURCES = g10.c \

1193
g10/apdu.c Normal file

File diff suppressed because it is too large Load Diff

74
g10/apdu.h Normal file
View File

@ -0,0 +1,74 @@
/* apdu.h - ISO 7816 APDU functions and low level I/O
* 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 APDU_H
#define APDU_H
/* ISO 7816 values for the statusword are defined here because they
should not be visible to the users of the actual ISO command
API. */
enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
masked of.*/
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_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00,
SW_CLA_NOT_SUP = 0x6e00,
SW_SUCCESS = 0x9000,
/* The follwoing statuswords are no real ones but used to map host
OS errors into status words. A status word is 16 bit so that
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_INCOMPLETE_CARD_RESPONSE = 0x10003,
SW_HOST_NO_DRIVER = 0x10004
};
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr);
unsigned char *apdu_get_atr (int slot, size_t *atrlen);
/* The apdu send functions do return status words. */
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,
int lc, const char *data,
unsigned char **retbuf, size_t *retbuflen);
int 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);
#endif /*APDU_H*/

76
g10/app-common.h Normal file
View File

@ -0,0 +1,76 @@
/* app-common.h
* 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 APP_COMMON_H
#define APP_COMMON_H
struct app_ctx_s {
int initialized; /* The application has been initialied and the
function pointers may be used. Note that for
unsupported operations the particular
function pointer is set to NULL */
int slot; /* Used reader. */
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */
unsigned int card_version;
int did_chv1;
int did_chv2;
int did_chv3;
struct {
int (*learn_status) (APP app, CTRL ctrl);
int (*setattr) (APP app, const char *name,
int (*pincb)(void*, const char *, char **),
void *pincb_arg,
const unsigned char *value, size_t valuelen);
int (*sign) (APP 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 (*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 (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,
const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*change_pin) (APP app, CTRL ctrl,
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
} fnc;
};
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);
#endif /*APP_COMMON_H*/

1430
g10/app-openpgp.c Normal file

File diff suppressed because it is too large Load Diff

720
g10/card-util.c Normal file
View File

@ -0,0 +1,720 @@
/* card-util.c - Utility functions for the OpenPGP card.
* 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>
#ifdef ENABLE_CARD_SUPPORT
/*
Note, that most card related code has been taken from 1.9.x branch
and is maintained over there if at all possible. Thus, if you make
changes here, please check that a similar change has been commited
to the 1.9.x branch.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include "util.h"
#include "i18n.h"
#include "ttyio.h"
#include "status.h"
#include "options.h"
#include "main.h"
#include "cardglue.h"
#define CONTROL_D ('D' - 'A' + 1)
/* Change the PIN of a an OpenPGP card. This is an interactive
function. */
void
change_pin (int chvno)
{
struct agent_card_info_s info;
int rc;
int reset_mode = 0;
rc = agent_learn (&info);
if (rc)
{
log_error (_("OpenPGP card not available: %s\n"),
gpg_strerror (rc));
return;
}
log_info (_("OpenPGP card no. %s detected\n"),
info.serialno? info.serialno : "[none]");
agent_release_card_info (&info);
if (opt.batch)
{
log_error (_("sorry, can't do this in batch mode\n"));
return;
}
for (;;)
{
char *answer;
tty_printf ("\n");
tty_printf ("1 - change signature PIN\n"
"2 - change decryption and authentication PIN\n"
"3 - change Admin's PIN\n"
"R - toggle reset retry counter mode\n"
"Q - quit\n");
tty_printf ("\n");
if (reset_mode)
{
tty_printf ("Reset Retry Counter mode active\n");
tty_printf ("\n");
}
answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
cpr_kill_prompt();
if (strlen (answer) != 1)
continue;
rc = 0;
if (reset_mode && *answer == '3')
{
tty_printf ("Sorry, reset of the Admin PIN's retry counter "
"is not possible.\n");
}
else if (*answer == '1' || *answer == '2' || *answer == '3')
{
rc = agent_scd_change_pin (*answer - '0' + (reset_mode?100:0));
if (rc)
tty_printf ("Error changing/resetting the PIN: %s\n",
gpg_strerror (rc));
else
tty_printf ("New PIN successfully set.\n");
}
else if (*answer == 'r' || *answer == 'R')
{
reset_mode = !reset_mode;
}
else if (*answer == 'q' || *answer == 'Q')
{
break;
}
}
}
static const char *
get_manufacturer (unsigned int no)
{
/* Note: Make sure that there is no colon or linefeed in the string. */
switch (no)
{
case 0:
case 0xffff: return "test card";
case 0x0001: return "PPC Card Systems";
default: return "unknown";
}
}
static void
print_sha1_fpr (FILE *fp, const unsigned char *fpr)
{
int i;
if (fpr)
{
for (i=0; i < 20 ; i+=2, fpr += 2 )
{
if (i == 10 )
tty_fprintf (fp, " ");
tty_fprintf (fp, " %02X%02X", *fpr, fpr[1]);
}
}
else
tty_fprintf (fp, " [none]");
tty_fprintf (fp, "\n");
}
static void
print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
{
int i;
if (fpr)
{
for (i=0; i < 20 ; i++, fpr++)
fprintf (fp, "%02X", *fpr);
}
putc (':', fp);
}
static void
print_name (FILE *fp, const char *text, const char *name)
{
tty_fprintf (fp, text);
/* FIXME: tty_printf_utf8_string2 eats everything after and
including an @ - e.g. when printing an url. */
if (name && *name)
{
if (fp)
print_utf8_string2 (fp, name, strlen (name), '\n');
else
tty_print_utf8_string2 (name, strlen (name), '\n');
}
else
tty_fprintf (fp, _("[not set]"));
tty_fprintf (fp, "\n");
}
static void
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);
if (name && *name)
{
char *p, *given, *buf = xstrdup (name);
given = strstr (buf, "<<");
for (p=buf; *p; p++)
if (*p == '<')
*p = ' ';
if (given && given[2])
{
*given = 0;
given += 2;
if (opt.with_colons)
print_string (fp, given, strlen (given), ':');
else if (fp)
print_utf8_string2 (fp, given, strlen (given), '\n');
else
tty_print_utf8_string2 (given, strlen (given), '\n');
if (opt.with_colons)
putc (':', fp);
else if (*buf)
tty_fprintf (fp, " ");
}
if (opt.with_colons)
print_string (fp, buf, strlen (buf), ':');
else if (fp)
print_utf8_string2 (fp, buf, strlen (buf), '\n');
else
tty_print_utf8_string2 (buf, strlen (buf), '\n');
xfree (buf);
}
else
{
if (opt.with_colons)
putc (':', fp);
else
tty_fprintf (fp, _("[not set]"));
}
if (opt.with_colons)
fputs (":\n", fp);
else
tty_fprintf (fp, "\n");
}
/* Print all available information about the current card. */
void
card_status (FILE *fp)
{
struct agent_card_info_s info;
PKT_public_key *pk = xcalloc (1, sizeof *pk);
int rc;
unsigned int uval;
rc = agent_learn (&info);
if (rc)
{
if (opt.with_colons)
fputs ("AID:::\n", fp);
log_error (_("OpenPGP card not available: %s\n"),
gpg_strerror (rc));
xfree (pk);
return;
}
if (opt.with_colons)
fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
else
tty_fprintf (fp, "Application ID ...: %s\n",
info.serialno? info.serialno : "[none]");
if (!info.serialno || strncmp (info.serialno, "D27600012401", 12)
|| strlen (info.serialno) != 32 )
{
if (opt.with_colons)
fputs ("unknown:\n", fp);
log_info ("not an OpenPGP card\n");
agent_release_card_info (&info);
xfree (pk);
return;
}
if (opt.with_colons)
fputs ("openpgp-card:\n", fp);
if (opt.with_colons)
{
fprintf (fp, "version:%.4s:\n", info.serialno+12);
uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
fprintf (fp, "serial:%.8s:\n", info.serialno+20);
print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
fputs ("lang:", fp);
if (info.disp_lang)
print_string (fp, info.disp_lang, strlen (info.disp_lang), ':');
fputs (":\n", fp);
fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
info.disp_sex == 2? 'f' : 'u'));
fputs ("url:", fp);
if (info.pubkey_url)
print_string (fp, info.pubkey_url, strlen (info.pubkey_url), ':');
fputs (":\n", fp);
fputs ("login:", fp);
if (info.login_data)
print_string (fp, info.login_data, strlen (info.login_data), ':');
fputs (":\n", fp);
fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
fprintf (fp, "maxpinlen:%d:%d:%d:\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
fprintf (fp, "pinretry:%d:%d:%d:\n",
info.chvretry[0], info.chvretry[1], info.chvretry[2]);
fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
fputs ("fpr:", fp);
print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
putc ('\n', fp);
}
else
{
tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
info.serialno[12] == '0'?"":info.serialno+12,
info.serialno[13],
info.serialno[14] == '0'?"":info.serialno+14,
info.serialno[15]);
tty_fprintf (fp, "Manufacturer .....: %s\n",
get_manufacturer (xtoi_2(info.serialno+16)*256
+ xtoi_2 (info.serialno+18)));
tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
print_name (fp, "Language prefs ...: ", info.disp_lang);
tty_fprintf (fp, "Sex ..............: %s\n",
info.disp_sex == 1? _("male"):
info.disp_sex == 2? _("female") : _("unspecified"));
print_name (fp, "URL of public key : ", info.pubkey_url);
print_name (fp, "Login data .......: ", info.login_data);
tty_fprintf (fp, "Signature PIN ....: %s\n",
info.chv1_cached? _("cached"): _("not cached"));
tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n",
info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
tty_fprintf (fp, "PIN retry counter : %d %d %d\n",
info.chvretry[0], info.chvretry[1], info.chvretry[2]);
tty_fprintf (fp, "Signature counter : %lu\n", info.sig_counter);
tty_fprintf (fp, "Signature key ....:");
print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
tty_fprintf (fp, "Encryption key....:");
print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
tty_fprintf (fp, "Authentication key:");
print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
/* tty_fprintf (fp, "General key info..: "); */
/* if (info.fpr1valid && !get_pubkey_byfprint (pk, info.fpr1, 20)) */
/* print_pubkey_info (fp, pk); */
/* else */
/* tty_fprintf (fp, "[none]\n"); */
}
free_public_key (pk);
agent_release_card_info (&info);
}
static char *
get_one_name (const char *prompt1, const char *prompt2)
{
char *name;
int i;
for (;;)
{
name = cpr_get (prompt1, prompt2);
if (!name)
return NULL;
trim_spaces (name);
cpr_kill_prompt ();
for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
;
/* The name must be in Latin-1 and not UTF-8 - lacking the code
to ensure this we restrict it to ASCII. */
if (name[i])
tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
else if (strchr (name, '<'))
tty_printf (_("Error: The \"<\" character may not be used.\n"));
else if (strstr (name, " "))
tty_printf (_("Error: Double spaces are not allowed.\n"));
else
return name;
xfree (name);
}
}
static int
change_name (void)
{
char *surname = NULL, *givenname = NULL;
char *isoname, *p;
int rc;
surname = get_one_name ("keygen.smartcard.surname",
_("Cardholder's surname: "));
givenname = get_one_name ("keygen.smartcard.givenname",
_("Cardholder's given name: "));
if (!surname || !givenname || (!*surname && !*givenname))
{
xfree (surname);
xfree (givenname);
return -1; /*canceled*/
}
isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
xfree (surname);
xfree (givenname);
for (p=isoname; *p; p++)
if (*p == ' ')
*p = '<';
log_debug ("setting Name to `%s'\n", isoname);
rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname) );
if (rc)
log_error ("error setting Name: %s\n", gpg_strerror (rc));
xfree (isoname);
return rc;
}
static int
change_url (void)
{
char *url;
int rc;
url = cpr_get ("cardedit.change_url", _("URL to retrieve public key: "));
if (!url)
return -1;
trim_spaces (url);
cpr_kill_prompt ();
rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url) );
if (rc)
log_error ("error setting URL: %s\n", gpg_strerror (rc));
xfree (url);
return rc;
}
static int
change_login (void)
{
char *data;
int rc;
data = cpr_get ("cardedit.change_login",
_("Login data (account name): "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
rc = agent_scd_setattr ("LOGIN-DATA", data, strlen (data) );
if (rc)
log_error ("error setting login data: %s\n", gpg_strerror (rc));
xfree (data);
return rc;
}
static int
change_lang (void)
{
char *data, *p;
int rc;
data = cpr_get ("cardedit.change_lang",
_("Language preferences: "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
if (strlen (data) > 8 || (strlen (data) & 1))
{
tty_printf (_("Error: invalid length of preference string.\n"));
xfree (data);
return -1;
}
for (p=data; *p && *p >= 'a' && *p <= 'z'; p++)
;
if (*p)
{
tty_printf (_("Error: invalid characters in preference string.\n"));
xfree (data);
return -1;
}
rc = agent_scd_setattr ("DISP-LANG", data, strlen (data) );
if (rc)
log_error ("error setting lang: %s\n", gpg_strerror (rc));
xfree (data);
return rc;
}
static int
change_sex (void)
{
char *data;
const char *str;
int rc;
data = cpr_get ("cardedit.change_sex",
_("Sex ((M)ale, (F)emale or space): "));
if (!data)
return -1;
trim_spaces (data);
cpr_kill_prompt ();
if (!*data)
str = "9";
else if ((*data == 'M' || *data == 'm') && !data[1])
str = "1";
else if ((*data == 'F' || *data == 'f') && !data[1])
str = "2";
else
{
tty_printf (_("Error: invalid response.\n"));
xfree (data);
return -1;
}
rc = agent_scd_setattr ("DISP-SEX", str, 1 );
if (rc)
log_error ("error setting sex: %s\n", gpg_strerror (rc));
xfree (data);
return rc;
}
/* Menu to edit all user changeable values on an OpenPGP card. Only
Key creation is not handled here. */
void
card_edit (STRLIST commands)
{
enum cmdids {
cmdNOP = 0,
cmdQUIT, cmdHELP, cmdLIST, cmdDEBUG,
cmdNAME, cmdURL, cmdLOGIN, cmdLANG, cmdSEX,
cmdINVCMD
};
static struct {
const char *name;
enum cmdids id;
const char *desc;
} cmds[] = {
{ N_("quit") , cmdQUIT , N_("quit this menu") },
{ N_("q") , cmdQUIT , NULL },
{ N_("help") , cmdHELP , N_("show this help") },
{ "?" , cmdHELP , NULL },
{ N_("list") , cmdLIST , N_("list all available data") },
{ N_("l") , cmdLIST , NULL },
{ N_("debug") , cmdDEBUG , NULL },
{ N_("name") , cmdNAME , N_("change card holder's name") },
{ N_("url") , cmdURL , N_("change URL to retrieve key") },
{ N_("login") , cmdLOGIN , N_("change the login name") },
{ N_("lang") , cmdLANG , N_("change the language preferences") },
{ N_("sex") , cmdSEX , N_("change card holder's sex") },
{ NULL, cmdINVCMD }
};
enum cmdids cmd = cmdNOP;
int have_commands = !!commands;
int redisplay = 1;
char *answer = NULL;
if (opt.command_fd != -1)
;
else if (opt.batch && !have_commands)
{
log_error(_("can't do that in batchmode\n"));
goto leave;
}
for (;;)
{
int arg_number;
const char *arg_string = "";
char *p;
int i;
tty_printf("\n");
if (redisplay )
{
if (opt.with_colons)
{
card_status (stdout);
fflush (stdout);
}
else
{
card_status (NULL);
tty_printf("\n");
}
redisplay = 0;
}
do
{
xfree (answer);
if (have_commands)
{
if (commands)
{
answer = xstrdup (commands->d);
commands = commands->next;
}
else if (opt.batch)
{
answer = xstrdup ("quit");
}
else
have_commands = 0;
}
if (!have_commands)
{
answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
cpr_kill_prompt();
}
trim_spaces(answer);
}
while( *answer == '#' );
arg_number = 0; /* Yes, here is the init which egcc complains about */
if (!*answer)
cmd = cmdLIST; /* Default to the list command */
else if (*answer == CONTROL_D)
cmd = cmdQUIT;
else {
if ((p=strchr (answer,' ')))
{
*p++ = 0;
trim_spaces (answer);
trim_spaces (p);
arg_number = atoi(p);
arg_string = p;
}
for (i=0; cmds[i].name; i++ )
if (!ascii_strcasecmp (answer, cmds[i].name ))
break;
cmd = cmds[i].id;
}
switch (cmd)
{
case cmdHELP:
for (i=0; cmds[i].name; i++ )
if (cmds[i].desc)
tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
break;
case cmdLIST:
redisplay = 1;
break;
case cmdNAME:
change_name ();
break;
case cmdURL:
change_url ();
break;
case cmdLOGIN:
change_login ();
break;
case cmdLANG:
change_lang ();
break;
case cmdSEX:
change_sex ();
break;
case cmdQUIT:
goto leave;
case cmdNOP:
break;
case cmdINVCMD:
default:
tty_printf ("\n");
tty_printf (_("Invalid command (try \"help\")\n"));
break;
} /* End command switch. */
} /* End of main menu loop. */
leave:
xfree (answer);
}
#endif /*ENABLE_CARD_SUPPORT*/

View File

@ -30,6 +30,7 @@
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <stdarg.h>
#include <assert.h>
#include "options.h"
@ -42,6 +43,17 @@
#include "i18n.h"
#include "cardglue.h"
#include "apdu.h"
#include "app-common.h"
struct ctrl_ctx_s {
int (*status_cb)(void *opaque, const char *line);
void *status_cb_arg;
};
static char *default_reader_port;
@ -70,12 +82,433 @@ serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
}
/* Send a line with status information via assuan and escape all given
buffers. The variable elements are pairs of (char *, size_t),
terminated with a (NULL, 0). */
void
send_status_info (CTRL ctrl, const char *keyword, ...)
{
va_list arg_ptr;
const unsigned char *value;
size_t valuelen;
char buf[950], *p;
size_t n;
va_start (arg_ptr, keyword);
p = buf;
n = 0;
valuelen = strlen (keyword);
for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, keyword++)
*p++ = *keyword;
while ( (value = va_arg (arg_ptr, const unsigned char *)) )
{
valuelen = va_arg (arg_ptr, size_t);
if (!valuelen)
continue; /* empty buffer */
if (n)
{
*p++ = ' ';
n++;
}
for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
{
if (*value < ' ' || *value == '+')
{
sprintf (p, "%%%02X", *value);
p += 3;
}
else if (*value == ' ')
*p++ = '+';
else
*p++ = *value;
}
}
*p = 0;
ctrl->status_cb (ctrl->status_cb_arg, buf);
va_end (arg_ptr);
}
void gcry_md_hash_buffer (int algo, void *digest,
const void *buffer, size_t length)
{
MD_HANDLE h = md_open (algo, 0);
if (!h)
BUG();
md_write (h, (byte *) buffer, length);
md_final (h);
memcpy (digest, md_read (h, algo), md_digest_length (algo));
md_close (h);
}
/* This is a limited version of the one in 1.9 but it should be
sufficient here. */
void
log_printf (const char *fmt, ...)
{
va_list arg_ptr;
va_start (arg_ptr, fmt);
vfprintf (log_stream (), fmt, arg_ptr);
va_end (arg_ptr);
}
/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
dump, with TEXT just an empty string, print a trailing linefeed,
otherwise print an entire debug line. */
void
log_printhex (const char *text, const void *buffer, size_t length)
{
if (text && *text)
log_debug ("%s ", text);
if (length)
{
const unsigned char *p = buffer;
log_printf ("%02X", *p);
for (length--, p++; length--; p++)
log_printf (" %02X", *p);
}
if (text)
log_printf ("\n");
}
void
app_set_default_reader_port (const char *portstr)
{
xfree (default_reader_port);
default_reader_port = portstr? xstrdup (portstr): NULL;
}
/* Retrieve the serial number and the time of the last update of the
card. The serial number is returned as a malloced string (hex
encoded) in SERIAL and the time of update is returned in STAMP. If
no update time is available the returned value is 0. Caller must
free SERIAL unless the function returns an error. */
int
app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
{
unsigned char *buf, *p;
int i;
if (!app || !serial || !stamp)
return gpg_error (GPG_ERR_INV_VALUE);
*serial = NULL;
*stamp = 0; /* not available */
buf = xtrymalloc (app->serialnolen * 2 + 1);
if (!buf)
return gpg_error_from_errno (errno);
for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
sprintf (p, "%02X", app->serialno[i]);
*p = 0;
*serial = buf;
return 0;
}
/* Release the card info structure. */
void
agent_release_card_info (struct agent_card_info_s *info)
{
if (!info)
return;
xfree (info->serialno); info->serialno = NULL;
xfree (info->disp_name); info->disp_name = NULL;
xfree (info->disp_lang); info->disp_lang = NULL;
xfree (info->pubkey_url); info->pubkey_url = NULL;
xfree (info->login_data); info->login_data = NULL;
info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
}
/* Open the current card and select the openpgp application. Return
an APP context handle to be used for further procesing or NULL on
error or if no OpenPGP application exists.*/
static APP
open_card (void)
{
int slot;
int rc;
APP app;
slot = apdu_open_reader (default_reader_port);
if (slot == -1)
{
log_error ("card reader not available\n");
return NULL;
}
app = xcalloc (1, sizeof *app);
app->slot = slot;
rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
if (rc)
{
/* apdu_close_reader (slot); */
log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
xfree (app);
return NULL;
}
app->initialized = 1;
return app;
}
/* Return a new malloced string by unescaping the string S. Escaping
is percent escaping and '+'/space mapping. A binary nul will
silently be replaced by a 0xFF. Function returns NULL to indicate
an out of memory status. */
static char *
unescape_status_string (const unsigned char *s)
{
char *buffer, *d;
buffer = d = xmalloc (strlen (s)+1);
while (*s)
{
if (*s == '%' && s[1] && s[2])
{
s++;
*d = xtoi_2 (s);
if (!*d)
*d = '\xff';
d++;
s += 2;
}
else if (*s == '+')
{
*d++ = ' ';
s++;
}
else
*d++ = *s++;
}
*d = 0;
return buffer;
}
/* Take a 20 byte hexencoded string and put it into the the provided
20 byte buffer FPR in binary format. */
static int
unhexify_fpr (const char *hexstr, unsigned char *fpr)
{
const char *s;
int n;
for (s=hexstr, n=0; hexdigitp (s); s++, n++)
;
if (*s || (n != 40))
return 0; /* no fingerprint (invalid or wrong length). */
n /= 2;
for (s=hexstr, n=0; *s; s += 2, n++)
fpr[n] = xtoi_2 (s);
return 1; /* okay */
}
/* Take the serial number from LINE and return it verbatim in a newly
allocated string. We make sure that only hex characters are
returned. */
static char *
store_serialno (const char *line)
{
const char *s;
char *p;
for (s=line; hexdigitp (s); s++)
;
p = xmalloc (s + 1 - line);
memcpy (p, line, s-line);
p[s-line] = 0;
return p;
}
static int
learn_status_cb (void *opaque, const char *line)
{
struct agent_card_info_s *parm = opaque;
const char *keyword = line;
int keywordlen;
int i;
for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
;
while (spacep (line))
line++;
if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
{
parm->serialno = store_serialno (line);
}
else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
{
parm->disp_name = unescape_status_string (line);
}
else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
{
parm->disp_lang = unescape_status_string (line);
}
else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
{
parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
}
else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
{
parm->pubkey_url = unescape_status_string (line);
}
else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
{
parm->login_data = unescape_status_string (line);
}
else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
{
parm->sig_counter = strtoul (line, NULL, 0);
}
else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
{
char *p, *buf;
buf = p = unescape_status_string (line);
if (buf)
{
while (spacep (p))
p++;
parm->chv1_cached = atoi (p);
while (!spacep (p))
p++;
while (spacep (p))
p++;
for (i=0; *p && i < 3; i++)
{
parm->chvmaxlen[i] = atoi (p);
while (!spacep (p))
p++;
while (spacep (p))
p++;
}
for (i=0; *p && i < 3; i++)
{
parm->chvretry[i] = atoi (p);
while (!spacep (p))
p++;
while (spacep (p))
p++;
}
xfree (buf);
}
}
else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
{
int no = atoi (line);
while (!spacep (line))
line++;
while (spacep (line))
line++;
if (no == 1)
parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
else if (no == 2)
parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
else if (no == 3)
parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
}
return 0;
}
/* Return card info. */
int
agent_learn (struct agent_card_info_s *info)
{
APP app;
int rc;
struct ctrl_ctx_s ctrl;
time_t stamp;
char *serial;
app = open_card ();
if (!app)
return gpg_error (GPG_ERR_CARD);
memset (&ctrl, 0, sizeof ctrl);
ctrl.status_cb = learn_status_cb;
ctrl.status_cb_arg = info;
rc = app_get_serial_and_stamp (app, &serial, &stamp);
if (!rc)
{
send_status_info (&ctrl, "SERIALNO", serial, strlen(serial), NULL, 0);
xfree (serial);
rc = app->fnc.learn_status (app, &ctrl);
}
return rc;
}
/* Send a SETATTR command to the SCdaemon. */
int
agent_scd_setattr (const char *name,
const unsigned char *value, size_t valuelen)
{
return gpg_error (GPG_ERR_CARD);
}
/* Send a GENKEY command to the SCdaemon. */
int
agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force)
{
return gpg_error (GPG_ERR_CARD);
}
/* Send a PKSIGN command to the SCdaemon. */
int
agent_scd_pksign (const char *keyid, int hashalgo,
const unsigned char *indata, size_t indatalen,
char **r_buf, size_t *r_buflen)
{
return gpg_error (GPG_ERR_CARD);
}
/* Send a PKDECRYPT command to the SCdaemon. */
int
agent_scd_pkdecrypt (const char *serialno,
const unsigned char *indata, size_t indatalen,
char **r_buf, size_t *r_buflen)
{
return gpg_error (GPG_ERR_CARD);
}
/* Change the PIN of an OpenPGP card or reset the retry counter. */
int
agent_scd_change_pin (int chvno)
{
return gpg_error (GPG_ERR_CARD);
}
#endif /*ENABLE_CARD_SUPPORT*/

View File

@ -60,13 +60,64 @@ 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;
#define GPG_ERR_BAD_PIN G10ERR_BAD_PASS
#define GPG_ERR_CARD G10ERR_GENERAL
#define GPG_ERR_EEXIST G10ERR_FILE_EXISTS
#define GPG_ERR_ENOMEM G10ERR_RESOURCE_LIMIT
#define GPG_ERR_GENERAL G10ERR_GENERAL
#define GPG_ERR_HARDWARE G10ERR_GENERAL
#define GPG_ERR_INV_CARD G10ERR_GENERAL
#define GPG_ERR_INV_ID G10ERR_GENERAL
#define GPG_ERR_INV_NAME G10ERR_GENERAL
#define GPG_ERR_INV_VALUE G10ERR_INV_ARG
#define GPG_ERR_NOT_SUPPORTED G10ERR_UNSUPPORTED
#define GPG_ERR_NO_OBJ G10ERR_GENERAL
#define GPG_ERR_PIN_BLOCKED G10ERR_PASSPHRASE
#define GPG_ERR_UNSUPPORTED_ALGORITHM G10ERR_PUBKEY_ALGO
#define GPG_ERR_USE_CONDITIONS G10ERR_GENERAL
#define GPG_ERR_WRONG_CARD G10ERR_GENERAL
#define GPG_ERR_WRONG_SECKEY G10ERR_WRONG_SECKEY
typedef int gpg_error_t;
typedef int gpg_err_code_t;
#define gpg_error(n) (n)
#define gpg_err_code(n) (n)
#define gpg_strerror(n) g10_errstr ((n))
#define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
/* We are not using it in a library, so we even let xtrymalloc
abort. Because we won't never return from these malloc functions,
we also don't need the out_of_core function, we simply define it to
return -1 */
#define xtrymalloc(n) xmalloc((n))
#define xtrycalloc(n,m) xcalloc((n),(m))
#define xtryrealloc(n,m) xrealloc((n),(m))
#define out_of_core() (-1)
#define gnupg_get_time() make_timestamp ()
char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
PKT_secret_key *sk);
void send_status_info (CTRL ctrl, const char *keyword, ...);
void gcry_md_hash_buffer (int algo, void *digest,
const void *buffer, size_t length);
void log_printf (const char *fmt, ...);
void log_printhex (const char *text, const void *buffer, size_t length);
#define GCRY_MD_SHA1 DIGEST_ALGO_SHA1
#define GCRY_MD_RMD160 DIGEST_ALGO_RMD160
/* Release the card info structure. */

990
g10/ccid-driver.c Normal file
View File

@ -0,0 +1,990 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
* Copyright (C) 2003 Free Software Foundation, Inc.
* Written by Werner Koch.
*
* 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
*
* ALTERNATIVELY, this file may be distributed under the terms of the
* following license, in which case the provisions of this license are
* required INSTEAD OF the GNU General Public License. If you wish to
* allow use of your version of this file only under the terms of the
* GNU General Public License, and not to allow others to use your
* version of this file under the terms of the following license,
* indicate your decision by deleting this paragraph and the license
* below.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/* CCID (ChipCardInterfaceDevices) is a specification for accessing
smartcard via a reader connected to the USB.
This is a limited driver allowing to use some CCID drivers directly
without any other specila drivers. This is a fallback driver to be
used when nothing else works or the system should be kept minimal
for security reasons. It makes use of the libusb library to gain
portable access to USB.
This driver has been tested with the SCM SCR335 smartcard reader
and requires that reader implements the TPDU level exchange and
does fully automatic initialization.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#if defined(HAVE_LIBUSB) || defined(TEST)
#define GNUPG_DEFAULT_SCDAEMON 1 /* Hack for 1.3 */
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <usb.h>
#include "ccid-driver.h"
#define DRVNAME "ccid-driver: "
#ifdef GNUPG_DEFAULT_SCDAEMON /* This source is used within the
gnupg>=1.9 source tree. */
# include "options.h"
# include "util.h"
# include "memory.h"
# define DEBUGOUT(t) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t); } while (0)
# define DEBUGOUT_1(t,a) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t,(a)); } while (0)
# define DEBUGOUT_2(t,a,b) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t,(a),(b)); } while (0)
# define DEBUGOUT_3(t,a,b,c) do { if (DBG_CARD_IO) \
log_debug (DRVNAME t,(a),(b),(c));} while (0)
# define DEBUGOUT_CONT(t) do { if (DBG_CARD_IO) \
log_printf (t); } while (0)
# define DEBUGOUT_CONT_1(t,a) do { if (DBG_CARD_IO) \
log_printf (t,(a)); } while (0)
# define DEBUGOUT_CONT_2(t,a,b) do { if (DBG_CARD_IO) \
log_printf (t,(a),(b)); } while (0)
# define DEBUGOUT_CONT_3(t,a,b,c) do { if (DBG_CARD_IO) \
log_printf (t,(a),(b),(c)); } while (0)
# define DEBUGOUT_LF() do { if (DBG_CARD_IO) \
log_printf ("\n"); } while (0)
#else /* Other usage of this source - don't use gnupg specifics. */
# define DEBUGOUT(t) fprintf (stderr, DRVNAME t)
# define DEBUGOUT_1(t,a) fprintf (stderr, DRVNAME t, (a))
# define DEBUGOUT_2(t,a,b) fprintf (stderr, DRVNAME t, (a), (b))
# define DEBUGOUT_3(t,a,b,c) fprintf (stderr, DRVNAME t, (a), (b), (c))
# define DEBUGOUT_CONT(t) fprintf (stderr, t)
# define DEBUGOUT_CONT_1(t,a) fprintf (stderr, t, (a))
# define DEBUGOUT_CONT_2(t,a,b) fprintf (stderr, t, (a), (b))
# define DEBUGOUT_CONT_3(t,a,b,c) fprintf (stderr, t, (a), (b), (c))
# define DEBUGOUT_LF() putc ('\n', stderr)
#endif /* This source not used by scdaemon. */
enum {
RDR_to_PC_NotifySlotChange= 0x50,
RDR_to_PC_HardwareError = 0x51,
PC_to_RDR_SetParameters = 0x61,
PC_to_RDR_IccPowerOn = 0x62,
PC_to_RDR_IccPowerOff = 0x63,
PC_to_RDR_GetSlotStatus = 0x65,
PC_to_RDR_Secure = 0x69,
PC_to_RDR_T0APDU = 0x6a,
PC_to_RDR_Escape = 0x6b,
PC_to_RDR_GetParameters = 0x6c,
PC_to_RDR_ResetParameters = 0x6d,
PC_to_RDR_IccClock = 0x6e,
PC_to_RDR_XfrBlock = 0x6f,
PC_to_RDR_Mechanical = 0x71,
PC_to_RDR_Abort = 0x72,
PC_to_RDR_SetDataRate = 0x73,
RDR_to_PC_DataBlock = 0x80,
RDR_to_PC_SlotStatus = 0x81,
RDR_to_PC_Parameters = 0x82,
RDR_to_PC_Escape = 0x83,
RDR_to_PC_DataRate = 0x84
};
/* Store information on the driver's state. A pointer to such a
structure is used as handle for most functions. */
struct ccid_driver_s {
usb_dev_handle *idev;
int seqno;
unsigned char t1_ns;
unsigned char t1_nr;
};
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
static unsigned int
convert_le_u32 (const unsigned char *buf)
{
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
/* Parse a CCID descriptor, optionally print all available features
and test whether this reader is usable by this driver. Returns 0
if it is usable.
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)
{
unsigned int i;
unsigned int us;
int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
if (buflen < 54 || buf[0] < 54)
{
DEBUGOUT ("CCID device descriptor is too short\n");
return -1;
}
DEBUGOUT ("ChipCard Interface Descriptor:\n");
DEBUGOUT_1 (" bLength %5u\n", buf[0]);
DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]);
DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]);
if (buf[3] != 1 || buf[2] != 0)
DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)");
DEBUGOUT_LF ();
DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]);
DEBUGOUT_2 (" bVoltageSupport %5u %s\n",
buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
: buf[5] == 3? "1.8V":"?"));
us = convert_le_u32 (buf+6);
DEBUGOUT_1 (" dwProtocols %5u ", us);
if ((us & 1))
DEBUGOUT_CONT (" T=0");
if ((us & 2))
{
DEBUGOUT_CONT (" T=1");
have_t1 = 1;
}
if ((us & ~3))
DEBUGOUT_CONT (" (Invalid values detected)");
DEBUGOUT_LF ();
us = convert_le_u32(buf+10);
DEBUGOUT_1 (" dwDefaultClock %5u\n", us);
us = convert_le_u32(buf+14);
DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us);
DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]);
us = convert_le_u32(buf+19);
DEBUGOUT_1 (" dwDataRate %7u bps\n", us);
us = convert_le_u32(buf+23);
DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us);
DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]);
us = convert_le_u32(buf+28);
DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
us = convert_le_u32(buf+32);
DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
if ((us&1))
DEBUGOUT_CONT ( " 2-wire");
if ((us&2))
DEBUGOUT_CONT ( " 3-wire");
if ((us&4))
DEBUGOUT_CONT ( " I2C");
DEBUGOUT_LF ();
us = convert_le_u32(buf+36);
DEBUGOUT_1 (" dwMechanical %08X ", us);
if ((us & 1))
DEBUGOUT_CONT (" accept");
if ((us & 2))
DEBUGOUT_CONT (" eject");
if ((us & 4))
DEBUGOUT_CONT (" capture");
if ((us & 8))
DEBUGOUT_CONT (" lock");
DEBUGOUT_LF ();
us = convert_le_u32(buf+40);
DEBUGOUT_1 (" dwFeatures %08X\n", us);
if ((us & 0x0002))
{
DEBUGOUT (" Auto configuration based on ATR\n");
have_auto_conf = 1;
}
if ((us & 0x0004))
DEBUGOUT (" Auto activation on insert\n");
if ((us & 0x0008))
DEBUGOUT (" Auto voltage selection\n");
if ((us & 0x0010))
DEBUGOUT (" Auto clock change\n");
if ((us & 0x0020))
DEBUGOUT (" Auto baud rate change\n");
if ((us & 0x0040))
DEBUGOUT (" Auto parameter negotation made by CCID\n");
else if ((us & 0x0080))
DEBUGOUT (" Auto PPS made by CCID\n");
else if ((us & (0x0040 | 0x0080)))
DEBUGOUT (" WARNING: conflicting negotation features\n");
if ((us & 0x0100))
DEBUGOUT (" CCID can set ICC in clock stop mode\n");
if ((us & 0x0200))
DEBUGOUT (" NAD value other than 0x00 accpeted\n");
if ((us & 0x0400))
DEBUGOUT (" Auto IFSD exchange\n");
if ((us & 0x00010000))
{
DEBUGOUT (" TPDU level exchange\n");
have_tpdu = 1;
}
else if ((us & 0x00020000))
DEBUGOUT (" Short APDU level exchange\n");
else if ((us & 0x00040000))
DEBUGOUT (" Short and extended APDU level exchange\n");
else if ((us & 0x00070000))
DEBUGOUT (" WARNING: conflicting exchange levels\n");
us = convert_le_u32(buf+44);
DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
DEBUGOUT ( " bClassGetResponse ");
if (buf[48] == 0xff)
DEBUGOUT_CONT ("echo\n");
else
DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
DEBUGOUT ( " bClassEnvelope ");
if (buf[49] == 0xff)
DEBUGOUT_CONT ("echo\n");
else
DEBUGOUT_1 (" %02X\n", buf[48]);
DEBUGOUT ( " wlcdLayout ");
if (!buf[50] && !buf[51])
DEBUGOUT_CONT ("none\n");
else
DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);
DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
if ((buf[52] & 1))
DEBUGOUT_CONT ( " verification");
if ((buf[52] & 2))
DEBUGOUT_CONT ( " modification");
DEBUGOUT_LF ();
DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
if (buf[0] > 54) {
DEBUGOUT (" junk ");
for (i=54; i < buf[0]-54; i++)
DEBUGOUT_CONT_1 (" %02X", buf[i]);
DEBUGOUT_LF ();
}
if (!have_t1 || !have_tpdu || !have_auto_conf)
{
DEBUGOUT ("this drivers requires that the reader supports T=1, "
"TPDU level exchange and auto configuration - "
"this is not available\n");
return -1;
}
else
return 0;
}
/* Read the device information, return all required data and check
that the device is usable for us. Returns 0 on success or an error
code. */
static int
read_device_info (struct usb_device *dev)
{
int cfg_no;
for (cfg_no=0; cfg_no < dev->descriptor->bNumConfigurations; cfg_no++)
{
struct usb_config_descriptor *config = dev->config + cfg_no;
int ifc_no;
for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
{
struct usb_interface *interface = config->interface + ifc_no;
int set_no;
for (set_no=0; set_no < interface->num_altsetting; set_no++)
{
struct usb_interface_descriptor *ifcdesc
= interface->altsetting + set_no;
if (ifcdesc->bInterfaceClass == 11
&& ifcdesc->bInterfaceSubClass == 0
&& ifcdesc->bInterfaceProtocol == 0)
{
if (ifcdesc->extra)
{
if (!parse_ccid_descriptor (ifcdesc->extra,
ifcdesc->extralen))
return 0; /* okay. we can use it. */
}
}
}
}
}
return -1; /* No suitable device found. */
}
/* Open the reader with the internal number READERNO and return a a
pointer to be used as handle in HANDLE. Returns 0 on success. */
int
ccid_open_reader (ccid_driver_t *handle, int readerno)
{
static int initialized;
int rc;
usb_match_handle *match = NULL;
struct usb_device *dev = NULL;
usb_dev_handle *idev = NULL;
*handle = NULL;
if (!initialized)
{
usb_init ();
initialized = 1;
}
rc = usb_create_match (&match, -1, -1, 11, -1, -1);
if (rc)
{
DEBUGOUT_1 ("usb_create_match failed: %d\n", rc);
return -1;
}
while (usb_find_device(match, dev, &dev) >= 0)
{
DEBUGOUT_3 ("%-40s %04X/%04X\n", dev->filename,
dev->descriptor->idVendor, dev->descriptor->idProduct);
if (!readerno)
{
rc = read_device_info (dev);
if (rc)
{
DEBUGOUT ("device not supported\n");
goto leave;
}
rc = usb_open (dev, &idev);
if (rc)
{
DEBUGOUT_1 ("usb_open failed: %d\n", rc);
goto leave;
}
/* fixme: Do we need to claim and set the interface as
determined by read_device_info ()? */
rc = usb_claim_interface (idev, 0);
if (rc)
{
DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
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
structure and store them with the handle? */
break;
}
readerno--;
}
leave:
if (idev)
usb_close (idev);
/* fixme: Do we need to release dev or is it supposed to be a
shallow copy of the list created internally by usb_init ? */
usb_free_match (match);
return rc;
}
/* Return False if a card is present and powered. */
int
ccid_check_card_presence (ccid_driver_t handle)
{
return -1;
}
static void
set_msg_len (unsigned char *msg, unsigned int length)
{
msg[1] = length;
msg[2] = length >> 8;
msg[3] = length >> 16;
msg[4] = length >> 24;
}
/* Write a MSG of length MSGLEN to the designated bulk out endpoint.
Returns 0 on success. */
static int
bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
{
int rc;
rc = usb_bulk_write (handle->idev,
1, /*endpoint */
msg, msglen,
1000 /* ms timeout */);
if (rc == msglen)
return 0;
if (rc == -1)
DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
else
DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
return -1;
}
/* Read a maximum of LENGTH bytes from the bulk in endpoint into
BUFFER and return the actual read number if bytes in NREAD. SEQNO
is the sequence number used to send the request and EXPECTED_TYPE
the type of message we expect. Does checks on the ccid
header. Returns 0 on success. */
static int
bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno)
{
int i, rc;
size_t msglen;
rc = usb_bulk_read (handle->idev,
0x82,
buffer, length,
10000 /* ms timeout */ );
/* Fixme: instead of using a 10 second timeout we should better
handle the timeout here and retry if appropriate. */
if (rc < 0)
{
DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
return -1;
}
*nread = msglen = rc;
if (msglen < 10)
{
DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
return -1;
}
if (buffer[0] != expected_type)
{
DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
return -1;
}
if (buffer[5] != 0)
{
DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
return -1;
}
if (buffer[6] != seqno)
{
DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
seqno, buffer[6]);
return -1;
}
DEBUGOUT_3 ("status: %02X error: %02X clock-status: %02X\n"
" data:", buffer[7], buffer[8], buffer[9] );
for (i=10; i < msglen; i++)
DEBUGOUT_CONT_1 (" %02X", buffer[i]);
DEBUGOUT_LF ();
return 0;
}
/* experimental */
int
ccid_poll (ccid_driver_t handle)
{
int rc;
unsigned char msg[10];
size_t msglen;
int i, j;
rc = usb_bulk_read (handle->idev,
0x83,
msg, sizeof msg,
0 /* ms timeout */ );
if (rc < 0 && errno == ETIMEDOUT)
return 0;
if (rc < 0)
{
DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
return -1;
}
msglen = rc;
rc = 0;
if (msglen < 1)
{
DEBUGOUT ("intr-in msg too short\n");
return -1;
}
if (msg[0] == RDR_to_PC_NotifySlotChange)
{
DEBUGOUT ("notify slot change:");
for (i=1; i < msglen; i++)
for (j=0; j < 4; j++)
DEBUGOUT_CONT_3 (" %d:%c%c",
(i-1)*4+j,
(msg[i] & (1<<(j*2)))? 'p':'-',
(msg[i] & (2<<(j*2)))? '*':' ');
DEBUGOUT_LF ();
}
else if (msg[0] == RDR_to_PC_HardwareError)
{
DEBUGOUT ("hardware error occured\n");
}
else
{
DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
}
return 0;
}
int
ccid_slot_status (ccid_driver_t handle)
{
int rc;
unsigned char msg[100];
size_t msglen;
unsigned char seqno;
msg[0] = PC_to_RDR_GetSlotStatus;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 0; /* RFU */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, 0);
rc = bulk_out (handle, msg, 10);
if (rc)
return rc;
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
if (rc)
return rc;
return 0;
}
int
ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen)
{
int rc;
unsigned char msg[100];
size_t msglen;
unsigned char seqno;
msg[0] = PC_to_RDR_IccPowerOn;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
msg[8] = 0; /* RFU */
msg[9] = 0; /* RFU */
set_msg_len (msg, 0);
msglen = 10;
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock, seqno);
if (rc)
return rc;
if (atr)
{
size_t n = msglen - 10;
if (n > maxatrlen)
n = maxatrlen;
memcpy (atr, msg+10, n);
*atrlen = n;
}
return 0;
}
/*
Protocol T=1 overview
Block Structure:
Prologue Field:
1 byte Node Address (NAD)
1 byte Protocol Control Byte (PCB)
1 byte Length (LEN)
Information Field:
0-254 byte APDU or Control Information (INF)
Epilogue Field:
1 byte Error Detection Code (EDC)
NAD:
bit 7 unused
bit 4..6 Destination Node Address (DAD)
bit 3 unused
bit 2..0 Source Node Address (SAD)
If node adresses are not used, SAD and DAD should be set to 0 on
the first block sent to the card. If they are used they should
have different values (0 for one is okay); that first block sets up
the addresses of the nodes.
PCB:
Information Block (I-Block):
bit 7 0
bit 6 Sequence number (yep, that is modulo 2)
bit 5 Chaining flag
bit 4..0 reserved
Received-Ready Block (R-Block):
bit 7 1
bit 6 0
bit 5 0
bit 4 Sequence number
bit 3..0 0 = no error
1 = EDC or parity error
2 = other error
other values are reserved
Supervisory Block (S-Block):
bit 7 1
bit 6 1
bit 5 clear=request,set=response
bit 4..0 0 = resyncronisation request
1 = information field size request
2 = abort request
3 = extension of BWT request
4 = VPP error
other values are reserved
*/
int
ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp)
{
int rc;
unsigned char send_buffer[10+258], recv_buffer[10+258];
unsigned char *msg, *tpdu, *p;
size_t msglen, tpdulen, n;
unsigned char seqno;
int i;
unsigned char crc;
size_t dummy_nresp;
int sending = 1;
if (!nresp)
nresp = &dummy_nresp;
*nresp = 0;
/* Construct an I-Block. */
if (apdulen > 254)
return -1; /* Invalid length. */
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=0 */
tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
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;
for (;;)
{
msg[0] = PC_to_RDR_XfrBlock;
msg[5] = 0; /* slot */
msg[6] = seqno = handle->seqno++;
msg[7] = 4; /* bBWI */
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 ();
/* 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))); */
rc = bulk_out (handle, msg, msglen);
if (rc)
return rc;
msg = recv_buffer;
rc = bulk_in (handle, msg, sizeof recv_buffer, &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;
}
/* 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 */
/* ); */
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
if (sending)
{ /* last block sent was successful. */
handle->t1_ns ^= 1;
sending = 0;
}
if (!!(tpdu[1] & 0x40) != handle->t1_nr)
{ /* Reponse does not match our sequence number. */
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=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;
continue;
}
handle->t1_nr ^= 1;
p = tpdu + 3; /* Skip the prologue field. */
n = tpdulen - 3 - 1; /* Strip the epilogue field. */
/* fixme: verify the checksum. */
if (resp)
{
if (n > maxresplen)
{
DEBUGOUT ("provided buffer too short for received data\n");
return -1;
}
memcpy (resp, p, n);
resp += n;
*nresp += n;
maxresplen -= n;
}
if (!(tpdu[1] & 0x20))
return 0; /* No chaining requested - ready. */
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=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;
}
else if ((tpdu[1] & 0xc0) == 0x80)
{ /* This is a R-block. */
if ( (tpdu[1] & 0x0f))
{ /* Error: repeat last block */
msg = send_buffer;
}
else
{
DEBUGOUT ("unxpectec ACK R-block received\n");
return -1;
}
}
else
{ /* This is a S-block. */
DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
{ /* Wait time extension request. */
unsigned char bwi = tpdu[3];
msg = send_buffer;
tpdu = msg+10;
tpdu[0] = ((1 << 4) | 0); /* NAD: DAD=1, SAD=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;
DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
}
else
return -1;
}
} /* end T=1 protocol loop. */
return 0;
}
#ifdef TEST
int
main (int argc, char **argv)
{
int rc;
ccid_driver_t ccid;
rc = ccid_open_reader (&ccid, 0);
if (rc)
return 1;
ccid_poll (ccid);
fputs ("getting ATR ...\n", stderr);
rc = ccid_get_atr (ccid, NULL, 0, NULL);
if (rc)
return 1;
ccid_poll (ccid);
fputs ("getting slot status ...\n", stderr);
rc = ccid_slot_status (ccid);
if (rc)
return 1;
ccid_poll (ccid);
{
static unsigned char apdu[] = {
0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
rc = ccid_transceive (ccid,
apdu, sizeof apdu,
NULL, 0, NULL);
}
ccid_poll (ccid);
{
static unsigned char apdu[] = {
0, 0xCA, 0, 0x65, 254 };
rc = ccid_transceive (ccid,
apdu, sizeof apdu,
NULL, 0, NULL);
}
ccid_poll (ccid);
return 0;
}
/*
* Local Variables:
* compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
* End:
*/
#endif /*TEST*/
#endif /*HAVE_LIBUSB*/

74
g10/ccid-driver.h Normal file
View File

@ -0,0 +1,74 @@
/* ccid-driver.c - USB ChipCardInterfaceDevices driver
* 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
*
* ALTERNATIVELY, this file may be distributed under the terms of the
* following license, in which case the provisions of this license are
* required INSTEAD OF the GNU General Public License. If you wish to
* allow use of your version of this file only under the terms of the
* GNU General Public License, and not to allow others to use your
* version of this file under the terms of the following license,
* indicate your decision by deleting this paragraph and the license
* below.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef CCID_DRIVER_H
#define CCID_DRIVER_H
struct ccid_driver_s;
typedef struct ccid_driver_s *ccid_driver_t;
int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
int ccid_transceive (ccid_driver_t handle,
const unsigned char *apdu, size_t apdulen,
unsigned char *resp, size_t maxresplen, size_t *nresp);
#endif /*CCID_DRIVER_H*/

View File

@ -128,6 +128,9 @@ enum cmd_and_opt_values { aNull = 0,
aPipeMode,
aRebuildKeydbCaches,
aRefreshKeys,
aCardStatus,
aCardEdit,
aChangePIN,
oTextmode,
oNoTextmode,
@ -318,6 +321,12 @@ enum cmd_and_opt_values { aNull = 0,
oNoMangleDosFilenames,
oEnableProgressFilter,
oMultifile,
oReaderPort,
octapiDriver,
opcscDriver,
oDisableCCID,
aTest };
@ -365,6 +374,11 @@ static ARGPARSE_OPTS opts[] = {
{ aExportSecretSub, "export-secret-subkeys" , 256, "@" },
{ aImport, "import", 256 , N_("import/merge keys")},
{ aFastImport, "fast-import", 256 , "@"},
#ifdef ENABLE_CARD_SUPPORT
{ aCardStatus, "card-status", 256, N_("print the card status")},
{ aCardEdit, "card-edit", 256, N_("change data on a card")},
{ aChangePIN, "change-pin", 256, N_("change a card's PIN")},
#endif
{ aListPackets, "list-packets",256, "@"},
{ aExportOwnerTrust, "export-ownertrust", 256, "@"},
{ aImportOwnerTrust, "import-ownertrust", 256, "@"},
@ -622,6 +636,13 @@ static ARGPARSE_OPTS opts[] = {
{ oNoMangleDosFilenames, "no-mangle-dos-filenames", 0, "@" },
{ oEnableProgressFilter, "enable-progress-filter", 0, "@" },
{ oMultifile, "multifile", 0, "@" },
{ oReaderPort, "reader-port", 2, "@"},
{ octapiDriver, "ctapi-driver", 2, "@"},
{ opcscDriver, "pcsc-driver", 2, "@"},
{ oDisableCCID, "disable-ccidc", 0, "@"},
{0} };
@ -1392,6 +1413,18 @@ main( int argc, char **argv )
case aPipeMode: set_cmd( &cmd, aPipeMode); break;
case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break;
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus: set_cmd (&cmd, aCardStatus); break;
case aCardEdit: set_cmd (&cmd, aCardEdit); break;
case aChangePIN: set_cmd (&cmd, aChangePIN); break;
case oReaderPort:
app_set_default_reader_port (pargs.r.ret_str);
break;
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;
#endif /* ENABLE_CARD_SUPPORT*/
case oArmor: opt.armor = 1; opt.no_armor=0; break;
case oOutput: opt.outfile = pargs.r.ret_str; break;
case oQuiet: opt.quiet = 1; break;
@ -2828,6 +2861,36 @@ main( int argc, char **argv )
keydb_rebuild_caches ();
break;
#ifdef ENABLE_CARD_SUPPORT
case aCardStatus:
if (argc)
wrong_args ("--card-status");
card_status (stdout);
break;
case aCardEdit:
if (argc) {
sl = NULL;
for (argc--, argv++ ; argc; argc--, argv++)
append_to_strlist (&sl, *argv);
card_edit (sl);
free_strlist (sl);
}
else
card_edit (NULL);
break;
case aChangePIN:
if (!argc)
change_pin (0);
else if (argc == 1)
change_pin ( atoi (*argv));
else
wrong_args ("--change-pin [no]");
break;
#endif /* ENABLE_CARD_SUPPORT*/
case aListPackets:
opt.list_packets=2;
default:

385
g10/iso7816.c Normal file
View File

@ -0,0 +1,385 @@
/* 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>
#ifdef ENABLE_CARD_SUPPORT
/*
Note, that most of this code has been taken from 1.9.x branch
and is maintained over there if at all possible. Thus, if you make
changes here, please check that a similar change has been commited
to the 1.9.x branch.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "options.h"
#include "errors.h"
#include "memory.h"
#include "util.h"
#include "i18n.h"
#include "iso7816.h"
#include "apdu.h"
#define CMD_SELECT_FILE 0xA4
#define CMD_VERIFY 0x20
#define CMD_CHANGE_REFERENCE_DATA 0x24
#define CMD_RESET_RETRY_COUNTER 0x2C
#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
static gpg_error_t
map_sw (int sw)
{
gpg_err_code_t ec;
switch (sw)
{
case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
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_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;
case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break;
case SW_SUCCESS: ec = 0; break;
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;
default:
if ((sw & 0x010000))
ec = GPG_ERR_GENERAL; /* Should not happen. */
else if ((sw & 0xff00) == SW_MORE_DATA)
ec = 0; /* This should actually never been seen here. */
else
ec = GPG_ERR_CARD;
}
return gpg_error (ec);
}
/* 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. */
gpg_error_t
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);
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
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);
return map_sw (sw);
}
/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
0), a "change reference data" is done, otherwise an "exchange
reference data". The new reference data is expected in NEWCHV of
length NEWCHVLEN. */
gpg_error_t
iso7816_change_reference_data (int slot, int chvno,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen)
{
int sw;
char *buf;
if ((!oldchv && oldchvlen)
|| (oldchv && !oldchvlen)
|| !newchv || !newchvlen )
return gpg_error (GPG_ERR_INV_VALUE);
buf = xtrymalloc (oldchvlen + newchvlen);
if (!buf)
return out_of_core ();
if (oldchvlen)
memcpy (buf, oldchv, oldchvlen);
memcpy (buf+oldchvlen, newchv, newchvlen);
sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
xfree (buf);
return map_sw (sw);
}
gpg_error_t
iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen)
{
int sw;
if (!newchv || !newchvlen )
return gpg_error (GPG_ERR_INV_VALUE);
sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
2, chvno, newchvlen, newchv);
return map_sw (sw);
}
/* 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. */
gpg_error_t
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 map_sw (sw);
}
return 0;
}
/* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */
gpg_error_t
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);
return map_sw (sw);
}
/* 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. */
gpg_error_t
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 map_sw (sw);
}
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. */
gpg_error_t
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 map_sw (sw);
}
return 0;
}
gpg_error_t
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 map_sw (sw);
}
return 0;
}
static gpg_error_t
do_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 map_sw (sw);
}
return 0;
}
gpg_error_t
iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
return do_generate_keypair (slot, 0, data, datalen, result, resultlen);
}
gpg_error_t
iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
return do_generate_keypair (slot, 1, data, datalen, result, resultlen);
}
gpg_error_t
iso7816_get_challenge (int slot, int length, unsigned char *buffer)
{
int sw;
unsigned char *result;
size_t resultlen, n;
if (!buffer || length < 1)
return gpg_error (GPG_ERR_INV_VALUE);
do
{
result = NULL;
n = length > 254? 254 : length;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
n,
&result, &resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
return map_sw (sw);
}
if (resultlen > n)
resultlen = n;
memcpy (buffer, result, resultlen);
buffer += resultlen;
length -= resultlen;
xfree (result);
}
while (length > 0);
return 0;
}
#endif /*ENABLE_CARD_SUPPORT*/

58
g10/iso7816.h Normal file
View File

@ -0,0 +1,58 @@
/* 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
#include "cardglue.h"
gpg_error_t iso7816_select_application (int slot,
const char *aid, size_t aidlen);
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,
const char *oldchv, size_t oldchvlen,
const char *newchv, size_t newchvlen);
gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
const char *newchv, size_t newchvlen);
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_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,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer);
#endif /*ISO7816_H*/

View File

@ -182,6 +182,13 @@ struct {
int strict;
int mangle_dos_filenames;
int enable_progress_filter;
#ifdef ENABLE_CARD_SUPPORT
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. */
#endif /*ENABLE_CARD_SUPPORT*/
} opt;
@ -199,6 +206,7 @@ struct {
#define DBG_TRUST_VALUE 256 /* debug the trustdb */
#define DBG_HASHING_VALUE 512 /* debug hashing operations */
#define DBG_EXTPROG_VALUE 1024 /* debug external program calls */
#define DBG_CARD_IO_VALUE 2048
#define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
@ -207,6 +215,7 @@ struct {
#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE)
#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
#define GNUPG (opt.compliance==CO_GNUPG)
#define RFC1991 (opt.compliance==CO_RFC1991 || opt.compliance==CO_PGP2)