* command.c (cmd_pkauth): New.

(cmd_setdata): Check whether data was given at all to avoid
passing 0 to malloc.

* app.c (app_auth): New.
* app-openpgp.c (do_auth): New.
This commit is contained in:
Werner Koch 2003-07-23 07:13:05 +00:00
parent fbdee01db9
commit b8becef1cf
10 changed files with 668 additions and 82 deletions

View File

@ -1,3 +1,39 @@
2003-07-23 Werner Koch <wk@gnupg.org>
* command.c (cmd_pkauth): New.
(cmd_setdata): Check whether data was given at all to avoid
passing 0 to malloc.
* app.c (app_auth): New.
* app-openpgp.c (do_auth): New.
2003-07-22 Werner Koch <wk@gnupg.org>
* command.c (cmd_passwd): New.
* app.c (app_change_pin): New.
* app-openpgp.c (do_change_pin): New.
* iso7816.c (iso7816_reset_retry_counter): Implemented.
* sc-investigate.c (main): New option --gen-random.
* iso7816.c (iso7816_get_challenge): Don't create APDUs with a
length larger than 255.
2003-07-17 Werner Koch <wk@gnupg.org>
* command.c (cmd_random): New command RANDOM.
* iso7816.c (map_sw): New. Use it in this file to return
meaningful error messages. Changed all public fucntions to return
a gpg_error_t.
(iso7816_change_reference_data): New.
* apdu.c (apdu_open_reader): Use faked status words for soem
system errors.
2003-07-16 Werner Koch <wk@gnupg.org>
* apdu.c (apdu_send_simple): Use apdu_send_le so that we can
specify not to send Le as it should be.
2003-07-15 Werner Koch <wk@gnupg.org>
* Makefile.am: Add sc-copykeys program.

View File

@ -213,7 +213,7 @@ ct_activate_card (int reader)
/* Open a reader and return an internal handle for it. PORT is a
non-negative value with the port number of the reader. USB readers
do habe port numbers starting at 32769. */
do have port numbers starting at 32769. */
static int
open_ct_reader (int port)
{
@ -244,7 +244,7 @@ open_ct_reader (int port)
}
/* Actuall send the APDU of length APDULEN to SLOT and return a
/* Actually send the APDU of length APDULEN to SLOT and return a
maximum of *BUFLEN data in BUFFER, the actual retruned size will be
set to BUFLEN. Returns: CT API error code. */
static int
@ -356,9 +356,9 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifdef HAVE_CTAPI
return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
#elif defined(HAVE_PCSC)
return -1;
return SW_HOST_NO_DRIVER;
#else
return -1;
return SW_HOST_NO_DRIVER;
#endif
}
@ -369,7 +369,7 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
or -1 for an invalid SLOT or other non card related error. If
RETBUF is not NULL, it will receive an allocated buffer with the
returned data. The length of that data will be put into
*RETBUFLEN. The caller is reposnible for releasing the buffer even
*RETBUFLEN. The caller is reponsible for releasing the buffer even
in case of errors. */
int
apdu_send_le(int slot, int class, int ins, int p0, int p1,
@ -391,7 +391,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
if (le != -1 && (le > 256 || le < 1))
return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1))
return -1;
return SW_HOST_INV_VALUE;
apdulen = 0;
apdu[apdulen++] = class;
@ -414,7 +414,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
log_error ("apdu_send_simple(%d) failed: %s\n",
slot, error_string (slot, rc));
return -1;
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
/* store away the returned data but strip the statusword. */
@ -432,7 +432,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf)
return -1; /* fixme: this is actually out of core. */
return SW_HOST_OUT_OF_CORE;
*retbuflen = resultlen;
memcpy (*retbuf, result, resultlen);
}
@ -448,7 +448,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
*retbuf = p = xtrymalloc (bufsize);
if (!*retbuf)
return -1; /* fixme: this is actually out of core. */
return SW_HOST_OUT_OF_CORE;
assert (resultlen < bufsize);
memcpy (p, result, resultlen);
p += resultlen;
@ -472,7 +472,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
{
log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, error_string (slot, rc));
return -1;
return SW_HOST_INCOMPLETE_CARD_RESPONSE;
}
sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2;
@ -492,7 +492,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp)
return -1; /* fixme: actually this is out of core */
return SW_HOST_OUT_OF_CORE;
p = tmp + (p - *retbuf);
*retbuf = tmp;
}
@ -531,8 +531,8 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
caller is reponsible for releasing the buffer even in case of
errors. */
int
apdu_send(int slot, int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
apdu_send (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{
return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen);
@ -548,5 +548,9 @@ int
apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data)
{
return apdu_send (slot, class, ins, p0, p1, lc, data, NULL, NULL);
return apdu_send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL);
}

View File

@ -22,7 +22,7 @@
#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
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
@ -32,19 +32,31 @@ enum {
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
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,
};
/* Note , that apdu_open_reader returns no status word but -1 on error. */
int apdu_open_reader (int port);
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,
@ -56,3 +68,6 @@ int apdu_send_le (int slot, int class, int ins, int p0, int p1,
#endif /*APDU_H*/

View File

@ -44,6 +44,11 @@ struct app_ctx_s {
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,
@ -53,6 +58,10 @@ struct app_ctx_s {
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;
@ -71,6 +80,11 @@ int app_sign (APP app, const char *keyidstr, int hashalgo,
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen );
int app_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 app_decipher (APP app, const char *keyidstr,
int (pincb)(void*, const char *, char **),
void *pincb_arg,
@ -79,6 +93,11 @@ int app_decipher (APP app, const char *keyidstr,
int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **),
void *pincb_arg);
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);
/*-- app-openpgp.c --*/
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);

View File

@ -561,6 +561,100 @@ do_setattr (APP app, const char *name,
return rc;
}
/* Handle the PASSWD command. */
static int
do_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc = 0;
int chvno = atoi (chvnostr);
char *pinvalue;
if (reset_mode && chvno == 3)
{
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
else if (reset_mode || chvno == 3)
{
rc = pincb (pincb_arg, "Admin PIN", &pinvalue);
if (rc)
{
log_error ("error getting PIN: %s\n", gpg_strerror (rc));
goto leave;
}
rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
xfree (pinvalue);
if (rc)
{
log_error ("verify CHV3 failed: rc=%04X\n", rc);
goto leave;
}
}
else if (chvno == 1)
{
rc = pincb (pincb_arg, "Signature PIN", &pinvalue);
if (rc)
{
log_error ("error getting PIN: %s\n", gpg_strerror (rc));
goto leave;
}
rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
xfree (pinvalue);
if (rc)
{
log_error ("verify CHV1 failed: rc=%04X\n", rc);
goto leave;
}
}
else if (chvno == 2)
{
rc = pincb (pincb_arg, "Decryption PIN", &pinvalue);
if (rc)
{
log_error ("error getting PIN: %s\n", gpg_strerror (rc));
goto leave;
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
xfree (pinvalue);
if (rc)
{
log_error ("verify CHV2 failed: rc=%04X\n", rc);
goto leave;
}
}
else
{
rc = gpg_error (GPG_ERR_INV_ID);
goto leave;
}
rc = pincb (pincb_arg, chvno == 1? "New Signature PIN" :
chvno == 2? "New Decryption PIN" :
chvno == 3? "New Admin PIN" : "?", &pinvalue);
if (rc)
{
log_error ("error getting new PIN: %s\n", gpg_strerror (rc));
goto leave;
}
if (reset_mode)
rc = iso7816_reset_retry_counter (app->slot, 0x80 + chvno,
pinvalue, strlen (pinvalue));
else
rc = iso7816_change_reference_data (app->slot, 0x80 + chvno,
NULL, 0,
pinvalue, strlen (pinvalue));
xfree (pinvalue);
leave:
return rc;
}
/* Handle the GENKEY command. */
static int
@ -630,7 +724,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
}
xfree (buffer); buffer = NULL;
#if 0
#if 1
log_info ("please wait while key is being generated ...\n");
start_at = time (NULL);
rc = iso7816_generate_keypair
@ -816,7 +910,7 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
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, gpg
will detect the bodus signature anyway die to the
will detect a bogus signature anyway due to the
verify-after-signing feature. */
if (fpr)
{
@ -883,6 +977,107 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
return rc;
}
/* Compute a digital signature using the INTERNAL AUTHENTICATE command
on INDATA which is expected to be the raw message digest. For this
application the KEYIDSTR consists of the serialnumber and the
fingerprint delimited by a slash.
Note that this fucntion may return the error code
GPG_ERR_WRONG_CARD to indicate that the card currently present does
not match the one required for the requested action (e.g. the
serial number does not match). */
static int
do_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 rc;
unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
const char *s;
int n;
const char *fpr = NULL;
if (!keyidstr || !*keyidstr)
return gpg_error (GPG_ERR_INV_VALUE);
if (indatalen > 50) /* For a 1024 bit key. */
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 == '/')
fpr = s + 1;
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);
/* 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, 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;
}
if (!app->did_chv2)
{
char *pinvalue;
rc = pincb (pincb_arg, "Authentication/Decryption PIN", &pinvalue);
if (rc)
{
log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
return rc;
}
rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
xfree (pinvalue);
if (rc)
{
log_error ("verify CHV2 failed\n");
rc = gpg_error (GPG_ERR_GENERAL);
return rc;
}
app->did_chv2 = 1;
}
rc = iso7816_internal_authenticate (app->slot, indata, indatalen,
outdata, outdatalen);
return rc;
}
static int
do_decipher (APP app, const char *keyidstr,
@ -1017,7 +1212,9 @@ app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
app->fnc.setattr = do_setattr;
app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_pin;
}
leave:

View File

@ -28,6 +28,7 @@
#include "scdaemon.h"
#include "app-common.h"
#include "apdu.h"
#include "iso7816.h"
/* The select the best fitting application and return a context.
Returns NULL if no application was found or no card is present. */
@ -157,6 +158,34 @@ app_sign (APP app, const char *keyidstr, int hashalgo,
return rc;
}
/* Create the signature using the INTERNAL AUTHENTICATE command and
return the allocated result in OUTDATA. If a PIN is required the
PINCB will be used to ask for the PIN; it should return the PIN in
an allocated buffer and put it into PIN. */
int
app_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 rc;
if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.auth)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = app->fnc.auth (app, keyidstr,
pincb, pincb_arg,
indata, indatalen,
outdata, outdatalen);
if (opt.verbose)
log_info ("operation auth result: %s\n", gpg_strerror (rc));
return rc;
}
/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
If a PIN is required the PINCB will be used to ask for the PIN; it
@ -206,3 +235,44 @@ app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
return rc;
}
/* Perform a GET CHALLENGE operation. This fucntion is special as it
directly accesses the card without any application specific
wrapper. */
int
app_get_challenge (APP app, size_t nbytes, unsigned char *buffer)
{
if (!app || !nbytes || !buffer)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
return iso7816_get_challenge (app->slot, nbytes, buffer);
}
/* Perform a CHANGE REFERENCE DATA or RESET RETRY COUNTER operation. */
int
app_change_pin (APP app, CTRL ctrl, const char *chvnostr, int reset_mode,
int (*pincb)(void*, const char *, char **),
void *pincb_arg)
{
int rc;
if (!app || !chvnostr || !*chvnostr || !pincb)
return gpg_error (GPG_ERR_INV_VALUE);
if (!app->initialized)
return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
if (!app->fnc.change_pin)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = app->fnc.change_pin (app, ctrl, chvnostr, reset_mode, pincb, pincb_arg);
if (opt.verbose)
log_info ("operation change_pin result: %s\n", gpg_strerror (rc));
return rc;
}

View File

@ -507,6 +507,8 @@ cmd_setdata (ASSUAN_CONTEXT ctx, char *line)
;
if (*p)
return set_error (Parameter_Error, "invalid hexstring");
if (!n)
return set_error (Parameter_Error, "no data given");
if ((n&1))
return set_error (Parameter_Error, "odd number of digits");
n /= 2;
@ -607,6 +609,52 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
return map_to_assuan_status (rc);
}
/* PKAUTH <hexified_id>
*/
static int
cmd_pkauth (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc;
unsigned char *outdata;
size_t outdatalen;
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 = strdup (line);
if (!keyidstr)
return ASSUAN_Out_Of_Core;
rc = app_auth (ctrl->app_ctx,
keyidstr,
pin_cb, ctx,
ctrl->in_data.value, ctrl->in_data.valuelen,
&outdata, &outdatalen);
free (keyidstr);
if (rc)
{
log_error ("app_auth_sign failed: %s\n", gpg_strerror (rc));
}
else
{
rc = assuan_send_data (ctx, outdata, outdatalen);
xfree (outdata);
if (rc)
return rc; /* that is already an assuan error code */
}
return map_to_assuan_status (rc);
}
/* PKDECRYPT <hexified_id>
*/
@ -746,6 +794,85 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
}
/* RANDOM <nbytes>
Get NBYTES of random from the card and send them back as data.
*/
static int
cmd_random (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc;
size_t nbytes;
unsigned char *buffer;
if (!*line)
return set_error (Parameter_Error, "number of requested bytes missing");
nbytes = strtoul (line, NULL, 0);
if ((rc = open_card (ctrl)))
return rc;
if (!ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
buffer = xtrymalloc (nbytes);
if (!buffer)
return ASSUAN_Out_Of_Core;
rc = app_get_challenge (ctrl->app_ctx, nbytes, buffer);
if (!rc)
{
rc = assuan_send_data (ctx, buffer, nbytes);
xfree (buffer);
return rc; /* that is already an assuan error code */
}
xfree (buffer);
return map_to_assuan_status (rc);
}
/* PASSWD [--reset] <chvno>
Change the PIN or reset thye retry counter of the card holder
verfication vector CHVNO. */
static int
cmd_passwd (ASSUAN_CONTEXT ctx, char *line)
{
CTRL ctrl = assuan_get_pointer (ctx);
int rc;
char *chvnostr;
int reset_mode = has_option (line, "--reset");
/* Skip over options. */
while (*line == '-' && line[1] == '-')
{
while (!spacep (line))
line++;
while (spacep (line))
line++;
}
if (!*line)
return set_error (Parameter_Error, "no CHV number given");
chvnostr = line;
while (!spacep (line))
line++;
*line = 0;
if ((rc = open_card (ctrl)))
return rc;
if (!ctrl->app_ctx)
return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
rc = app_change_pin (ctrl->app_ctx, ctrl, chvnostr, reset_mode, pin_cb, ctx
);
if (rc)
log_error ("command passwd failed: %s\n", gpg_strerror (rc));
return map_to_assuan_status (rc);
}
@ -763,11 +890,14 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "READKEY", cmd_readkey },
{ "SETDATA", cmd_setdata },
{ "PKSIGN", cmd_pksign },
{ "PKAUTH", cmd_pkauth },
{ "PKDECRYPT", cmd_pkdecrypt },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "SETATTR", cmd_setattr },
{ "GENKEY", cmd_genkey },
{ "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd },
{ NULL }
};
int i, rc;

View File

@ -31,6 +31,8 @@
#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
@ -38,6 +40,40 @@
#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
@ -45,37 +81,78 @@
AIDs and won't return the AID on success. The return value is 0
for okay or GNUPG error code. Note that ISO error codes are
internally mapped. */
int
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);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
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. */
int
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);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
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. */
int
gpg_error_t
iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen)
{
@ -95,7 +172,7 @@ iso7816_get_data (int slot, int tag,
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
return 0;
@ -104,7 +181,7 @@ iso7816_get_data (int slot, int tag,
/* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */
int
gpg_error_t
iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen)
{
@ -113,10 +190,7 @@ iso7816_put_data (int slot, int tag,
sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
((tag >> 8) & 0xff), (tag & 0xff),
datalen, data);
if (sw == SW_SUCCESS)
return 0;
else
return -1; /* Fixme: we need a real error code. */
return map_sw (sw);
}
@ -124,7 +198,7 @@ iso7816_put_data (int slot, int tag,
success 0 is returned and the data is availavle in a newly
allocated buffer stored at RESULT with its length stored at
RESULTLEN. */
int
gpg_error_t
iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
@ -143,7 +217,7 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
return 0;
@ -154,7 +228,7 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
success 0 is returned and the plaintext is available in a newly
allocated buffer stored at RESULT with its length stored at
RESULTLEN. */
int
gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
{
@ -181,14 +255,14 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
return 0;
}
int
gpg_error_t
iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
@ -208,14 +282,14 @@ iso7816_internal_authenticate (int slot,
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
return 0;
}
static int
static gpg_error_t
generate_keypair (int slot, int readonly,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
@ -235,14 +309,14 @@ generate_keypair (int slot, int readonly,
xfree (*result);
*result = NULL;
*resultlen = 0;
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
return 0;
}
int
gpg_error_t
iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
@ -251,7 +325,7 @@ iso7816_generate_keypair (int slot,
}
int
gpg_error_t
iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen)
@ -261,12 +335,12 @@ iso7816_read_public_key (int slot,
int
iso1816_get_challenge (int slot, int length, unsigned char *buffer)
gpg_error_t
iso7816_get_challenge (int slot, int length, unsigned char *buffer)
{
int sw;
unsigned char *result;
size_t resultlen;
size_t resultlen, n;
if (!buffer || length < 1)
return gpg_error (GPG_ERR_INV_VALUE);
@ -274,26 +348,24 @@ iso1816_get_challenge (int slot, int length, unsigned char *buffer)
do
{
result = NULL;
n = length > 254? 254 : length;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
length,
n,
&result, &resultlen);
if (sw != SW_SUCCESS)
{
/* Make sure that pending buffers are released. */
xfree (result);
return -1; /* FIXME: Map error codes. */
return map_sw (sw);
}
if (resultlen > length)
resultlen = length;
if (resultlen > n)
resultlen = n;
memcpy (buffer, result, resultlen);
buffer += resultlen;
length -= length;
length -= resultlen;
xfree (result);
}
while (length > 0);
return 0;
}

View File

@ -21,28 +21,36 @@
#ifndef ISO7816_H
#define ISO7816_H
int iso7816_select_application (int slot, const char *aid, size_t aidlen);
int iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen);
int iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen);
int iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen);
int iso7816_compute_ds (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_decipher (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso7816_generate_keypair (int slot,
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);
int iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
int iso1816_get_challenge (int slot, int length, unsigned char *buffer);
gpg_error_t iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen);
gpg_error_t iso7816_get_challenge (int slot,
int length, unsigned char *buffer);
#endif /*ISO7816_H*/

View File

@ -23,6 +23,7 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#define JNLIB_NEED_LOG_LOGV
#include "scdaemon.h"
@ -41,6 +42,8 @@ enum cmd_and_opt_values
oDebug,
oDebugAll,
oGenRandom,
aTest };
@ -52,6 +55,7 @@ static ARGPARSE_OPTS opts[] = {
{ oReaderPort, "reader-port", 1, "|N|connect to reader at port N"},
{ oDebug, "debug" ,4|16, "set debugging flags"},
{ oDebugAll, "debug-all" ,0, "enable full debugging"},
{ oGenRandom, "gen-random", 4, "|N|generate N bytes of random"},
{0}
};
@ -106,6 +110,7 @@ main (int argc, char **argv )
int slot, rc;
int reader_port = 32768; /* First USB reader. */
struct app_ctx_s appbuf;
unsigned long gen_random = 0;
memset (&appbuf, 0, sizeof appbuf);
@ -134,6 +139,7 @@ main (int argc, char **argv )
case oVerbose: opt.verbose++; break;
case oDebug: opt.debug |= pargs.r.ret_ulong; break;
case oDebugAll: opt.debug = ~0; break;
case oGenRandom: gen_random = pargs.r.ret_ulong; break;
default : pargs.err = 2; break;
}
}
@ -149,20 +155,49 @@ main (int argc, char **argv )
slot = apdu_open_reader (reader_port);
if (slot == -1)
exit (1);
rc = atr_dump (slot, stdout);
if (rc)
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
if (!gen_random)
{
rc = atr_dump (slot, stdout);
if (rc)
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
}
appbuf.slot = slot;
rc = app_select_openpgp (&appbuf, NULL, NULL);
if (rc)
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
else
log_info ("openpgp application selected\n");
{
appbuf.initialized = 1;
log_info ("openpgp application selected\n");
if (gen_random)
{
size_t nbytes;
unsigned char *buffer;
buffer = xmalloc (4096);
do
{
nbytes = gen_random > 4096? 4096 : gen_random;
rc = app_get_challenge (&appbuf, nbytes, buffer);
if (rc)
log_error ("app_get_challenge failed: %s\n",gpg_strerror (rc));
else
{
if (fwrite (buffer, nbytes, 1, stdout) != 1)
log_error ("writing to stdout failed: %s\n",
strerror (errno));
gen_random -= nbytes;
}
}
while (gen_random && !log_get_errorcount (0));
xfree (buffer);
}
}
return 0;
return log_get_errorcount (0)? 2:0;
}