* command.c (cmd_checkpin): New.

(register_commands): Add command CHECKPIN.
* app.c (app_check_pin): New.
* app-openpgp.c (check_against_given_fingerprint): New. Factored
out that code elsewhere.
(do_check_pin): New.
This commit is contained in:
Werner Koch 2003-10-21 17:12:50 +00:00
parent 99277d21c1
commit 21be16dba9
9 changed files with 324 additions and 95 deletions

View File

@ -1,3 +1,20 @@
2003-10-20 Werner Koch <wk@gnupg.org>
* command.c (cmd_checkpin): New.
(register_commands): Add command CHECKPIN.
* app.c (app_check_pin): New.
* app-openpgp.c (check_against_given_fingerprint): New. Factored
out that code elsewhere.
(do_check_pin): New.
2003-10-10 Werner Koch <wk@gnupg.org>
* ccid-driver.c (ccid_close_reader): New.
* apdu.c (close_ccid_reader, close_ct_reader, close_csc_reader)
(close_osc_reader, apdu_close_reader): New. Not all are properly
implemented yet.
2003-10-09 Werner Koch <wk@gnupg.org>
* ccid-driver.c (ccid_transceive): Add T=1 chaining for sending.

View File

@ -51,6 +51,13 @@
insertion of the card (1 = don't wait). */
#ifdef _WIN32
#define DLSTDCALL __stdcall
#else
#define DLSTDCALL
#endif
/* A structure to collect information pertaining to one reader
slot. */
struct reader_table_s {
@ -84,12 +91,12 @@ static struct reader_table_s reader_table[MAX_READER];
/* ct API function pointer. */
static char (*CT_init) (unsigned short ctn, unsigned short Pn);
static char (*CT_data) (unsigned short ctn, unsigned char *dad,
unsigned char *sad, unsigned short lc,
unsigned char *cmd, unsigned short *lr,
unsigned char *rsp);
static char (*CT_close) (unsigned short ctn);
static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn);
static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad,
unsigned char *sad, unsigned short lc,
unsigned char *cmd, unsigned short *lr,
unsigned char *rsp);
static char (* DLSTDCALL CT_close) (unsigned short ctn);
/* PC/SC constants and function pointer. */
#define PCSC_SCOPE_USER 0
@ -117,34 +124,38 @@ struct pcsc_io_request_s {
typedef struct pcsc_io_request_s *pcsc_io_request_t;
long (*pcsc_establish_context) (unsigned long scope,
const void *reserved1,
const void *reserved2,
unsigned long *r_context);
long (*pcsc_release_context) (unsigned long context);
long (*pcsc_list_readers) (unsigned long context, const char *groups,
char *readers, unsigned long *readerslen);
long (*pcsc_connect) (unsigned long context,
const char *reader,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long *r_card,
unsigned long *r_active_protocol);
long (*pcsc_disconnect) (unsigned long card, unsigned long disposition);
long (*pcsc_status) (unsigned long card,
char *reader, unsigned long *readerlen,
unsigned long *r_state, unsigned long *r_protocol,
unsigned char *atr, unsigned long *atrlen);
long (*pcsc_begin_transaction) (unsigned long card);
long (*pcsc_end_transaction) (unsigned long card);
long (*pcsc_transmit) (unsigned long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
unsigned long send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
unsigned long *recv_len);
long (*pcsc_set_timeout) (unsigned long context, unsigned long timeout);
long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
const void *reserved1,
const void *reserved2,
unsigned long *r_context);
long (* DLSTDCALL pcsc_release_context) (unsigned long context);
long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
const char *groups,
char *readers, unsigned long*readerslen);
long (* DLSTDCALL pcsc_connect) (unsigned long context,
const char *reader,
unsigned long share_mode,
unsigned long preferred_protocols,
unsigned long *r_card,
unsigned long *r_active_protocol);
long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
unsigned long disposition);
long (* DLSTDCALL pcsc_status) (unsigned long card,
char *reader, unsigned long *readerlen,
unsigned long *r_state,
unsigned long *r_protocol,
unsigned char *atr, unsigned long *atrlen);
long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
long (* DLSTDCALL pcsc_end_transaction) (unsigned long card);
long (* DLSTDCALL pcsc_transmit) (unsigned long card,
const pcsc_io_request_t send_pci,
const unsigned char *send_buffer,
unsigned long send_len,
pcsc_io_request_t recv_pci,
unsigned char *recv_buffer,
unsigned long *recv_len);
long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
unsigned long timeout);
@ -347,6 +358,14 @@ open_ct_reader (int port)
return reader;
}
static int
close_ct_reader (int slot)
{
/* FIXME: Implement. */
reader_table[slot].used = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
@ -570,6 +589,17 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
return err? -1:0; /* FIXME: Return appropriate error code. */
}
static int
close_pcsc_reader (int slot)
{
/* FIXME: Implement. */
reader_table[slot].used = 0;
return 0;
}
#ifdef HAVE_LIBUSB
/*
@ -609,6 +639,15 @@ open_ccid_reader (void)
return slot;
}
static int
close_ccid_reader (int slot)
{
ccid_close_reader (reader_table[slot].ccid.handle);
reader_table[slot].used = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
@ -738,6 +777,16 @@ open_osc_reader (int portno)
}
static int
close_osc_reader (int slot)
{
/* FIXME: Implement. */
reader_table[slot].used = 0;
return 0;
}
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual returned size will be
set to BUFLEN. Returns: OpenSC error code. */
@ -940,6 +989,26 @@ apdu_open_reader (const char *portstr)
}
int
apdu_close_reader (int slot)
{
if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
return SW_HOST_NO_DRIVER;
if (reader_table[slot].is_ctapi)
return close_ct_reader (slot);
#ifdef HAVE_LIBUSB
else if (reader_table[slot].is_ccid)
return close_ccid_reader (slot);
#endif
#ifdef HAVE_OPENSC
else if (reader_table[slot].is_osc)
return close_osc_reader (slot);
#endif
else
return close_pcsc_reader (slot);
}
unsigned char *
apdu_get_atr (int slot, size_t *atrlen)
{

View File

@ -54,6 +54,7 @@ enum {
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (const char *portstr);
int apdu_close_reader (int slot);
unsigned char *apdu_get_atr (int slot, size_t *atrlen);

View File

@ -65,6 +65,9 @@ struct app_ctx_s {
const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int (*check_pin) (APP app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg);
} fnc;
@ -106,6 +109,9 @@ int app_get_challenge (APP app, size_t nbytes, unsigned char *buffer);
int app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
int app_check_pin (APP app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
/*-- app-openpgp.c --*/

View File

@ -912,6 +912,33 @@ compare_fingerprint (APP app, int keyno, unsigned char *sha1fpr)
}
/* If a fingerprint has been specified check it against the one on
the card. This is allows for a meaningful error message in case
the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint we
assume that this is okay. */
static int
check_against_given_fingerprint (APP app, const char *fpr, int keyno)
{
unsigned char tmp[20];
const char *s;
int n;
for (s=fpr, n=0; hexdigitp (s); s++, n++)
;
if (n != 40)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* okay */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=fpr, n=0; n < 20; s += 2, n++)
tmp[n] = xtoi_2 (s);
return compare_fingerprint (app, keyno, tmp);
}
/* Compute a digital signature on INDATA which is expected to be the
raw message digest. For this application the KEYIDSTR consists of
@ -976,23 +1003,9 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
known to gpg was not updated. If there is no fingerprint, gpg
will detect a bogus signature anyway due to the
verify-after-signing feature. */
if (fpr)
{
for (s=fpr, n=0; hexdigitp (s); s++, n++)
;
if (n != 40)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* okay */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=fpr, n=0; n < 20; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
rc = compare_fingerprint (app, 1, tmp_sn);
if (rc)
return rc;
}
rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0;
if (rc)
return rc;
if (hashalgo == GCRY_MD_SHA1)
memcpy (data, sha1_prefix, 15);
@ -1107,23 +1120,9 @@ do_auth (APP app, const char *keyidstr,
known to gpg was not updated. If there is no fingerprint, gpg
will detect a bogus signature anyway due to the
verify-after-signing feature. */
if (fpr)
{
for (s=fpr, n=0; hexdigitp (s); s++, n++)
;
if (n != 40)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* okay */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=fpr, n=0; n < 20; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
rc = compare_fingerprint (app, 3, tmp_sn);
if (rc)
return rc;
}
rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0;
if (rc)
return rc;
rc = verify_chv2 (app, pincb, pincb_arg);
if (!rc)
@ -1177,23 +1176,9 @@ do_decipher (APP app, const char *keyidstr,
the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint, the
decryption will won't produce the right plaintext anyway. */
if (fpr)
{
for (s=fpr, n=0; hexdigitp (s); s++, n++)
;
if (n != 40)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* okay */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=fpr, n=0; n < 20; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
rc = compare_fingerprint (app, 2, tmp_sn);
if (rc)
return rc;
}
rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
if (rc)
return rc;
rc = verify_chv2 (app, pincb, pincb_arg);
if (!rc)
@ -1202,6 +1187,55 @@ do_decipher (APP app, const char *keyidstr,
}
/* Perform a simple verify operation for CHV1 and CHV2, so that
further operations won't ask for CHV2 and it is possible to do a
cheap check on the PIN: If there is something wrong with the PIN
entry system, only the regular CHV will get blocked and not the
dangerous CHV3. KEYIDSTR is the usual card's serial number; an
optional fingerprint part will be ignored. */
static int
do_check_pin (APP app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg)
{
unsigned char tmp_sn[20];
const char *s;
int n;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
/* Check whether an OpenPGP card of any version has been requested. */
if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
;
if (n != 32)
return gpg_error (GPG_ERR_INV_ID);
else if (!*s)
; /* No fingerprint given: we allow this for now. */
else if (*s == '/')
; /* We ignore a fingerprint. */
else
return gpg_error (GPG_ERR_INV_ID);
for (s=keyidstr, n=0; n < 16; s += 2, n++)
tmp_sn[n] = xtoi_2 (s);
if (app->serialnolen != 16)
return gpg_error (GPG_ERR_INV_CARD);
if (memcmp (app->serialno, tmp_sn, 16))
return gpg_error (GPG_ERR_WRONG_CARD);
/* Yes, there is a race conditions: The user might pull the card
right here and we won't notice that. However this is not a
problem and the check above is merely for a graceful failure
between operations. */
return verify_chv2 (app, pincb, pincb_arg);
}
/* Select the OpenPGP application on the card in SLOT. This function
@ -1262,6 +1296,7 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_pin;
app->fnc.check_pin = do_check_pin;
}
leave:

View File

@ -295,6 +295,29 @@ app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
}
/* Perform a VERIFY operation without doing anything lese. This may
be used to initialze a the PION cache for long lasting other
operations. Its use is highly application dependent. */
int
app_check_pin (APP app, const char *keyidstr,
int (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc;
if (!app || !keyidstr || !*keyidstr || !pincb)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.check_pin)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = app->fnc.check_pin (app, keyidstr, pincb, pincb_arg);
if (opt.verbose)
log_info ("operation check_pin result: %s\n", gpg_strerror (rc));
return rc;
}

View File

@ -174,6 +174,12 @@ struct ccid_driver_s {
};
static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
size_t *nread, int expected_type, int seqno);
/* Convert a little endian stored 4 byte value into an unsigned
integer. */
static unsigned int
@ -182,6 +188,16 @@ convert_le_u32 (const unsigned char *buf)
return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
}
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;
}
/* Parse a CCID descriptor, optionally print all available features
@ -482,6 +498,41 @@ ccid_open_reader (ccid_driver_t *handle, int readerno)
}
/* Close the reader HANDLE. */
int
ccid_close_reader (ccid_driver_t handle)
{
if (!handle || !handle->idev)
return 0;
{
int rc;
unsigned char msg[100];
size_t msglen;
unsigned char seqno;
msg[0] = PC_to_RDR_IccPowerOff;
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);
msglen = 10;
rc = bulk_out (handle, msg, msglen);
if (!rc)
bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus, seqno);
}
usb_release_interface (handle->idev, 0);
usb_close (handle->idev);
handle->idev = NULL;
free (handle);
return 0;
}
/* Return False if a card is present and powered. */
int
ccid_check_card_presence (ccid_driver_t handle)
@ -491,16 +542,6 @@ ccid_check_card_presence (ccid_driver_t handle)
}
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

View File

@ -60,6 +60,7 @@ struct ccid_driver_s;
typedef struct ccid_driver_s *ccid_driver_t;
int ccid_open_reader (ccid_driver_t *handle, int readerno);
int ccid_close_reader (ccid_driver_t handle);
int ccid_get_atr (ccid_driver_t handle,
unsigned char *atr, size_t maxatrlen, size_t *atrlen);
int ccid_transceive (ccid_driver_t handle,

View File

@ -923,6 +923,41 @@ cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
}
/* CHECKPIN <hexified_id>
*/
static int
cmd_checkpin (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc;
char *keyidstr;
if ((rc = open_card (ctrl)))
return rc;
if (!ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
/* We have to use a copy of the key ID because the function may use
the pin_cb which in turn uses the assuan line buffer and thus
overwriting the original line with the keyid. */
keyidstr = xtrystrdup (line);
if (!keyidstr)
return ASSUAN_Out_Of_Core;
rc = app_check_pin (ctrl->app_ctx,
keyidstr,
pin_cb, ctx);
xfree (keyidstr);
if (rc)
log_error ("app_check_pin failed: %s\n", gpg_strerror (rc));
return map_to_assuan_status (rc);
}
/* Tell the assuan library about our commands */
@ -948,6 +983,7 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "GENKEY", cmd_genkey },
{ "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd },
{ "CHECKPIN", cmd_checkpin },
{ NULL }
};
int i, rc;