mirror of
git://git.gnupg.org/gnupg.git
synced 2025-02-21 19:48:05 +01:00
* 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:
parent
20ed53942a
commit
e369270a65
@ -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>
|
||||
|
||||
|
@ -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
1193
g10/apdu.c
Normal file
File diff suppressed because it is too large
Load Diff
74
g10/apdu.h
Normal file
74
g10/apdu.h
Normal 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
76
g10/app-common.h
Normal 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
1430
g10/app-openpgp.c
Normal file
File diff suppressed because it is too large
Load Diff
720
g10/card-util.c
Normal file
720
g10/card-util.c
Normal 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*/
|
433
g10/cardglue.c
433
g10/cardglue.c
@ -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*/
|
||||
|
@ -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
990
g10/ccid-driver.c
Normal 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
74
g10/ccid-driver.h
Normal 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*/
|
||||
|
||||
|
||||
|
63
g10/g10.c
63
g10/g10.c
@ -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
385
g10/iso7816.c
Normal 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
58
g10/iso7816.h
Normal 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*/
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user