* 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> 2003-07-15 Werner Koch <wk@gnupg.org>
* Makefile.am: Add sc-copykeys program. * 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 /* 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 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 static int
open_ct_reader (int port) 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 maximum of *BUFLEN data in BUFFER, the actual retruned size will be
set to BUFLEN. Returns: CT API error code. */ set to BUFLEN. Returns: CT API error code. */
static int static int
@ -356,9 +356,9 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen,
#ifdef HAVE_CTAPI #ifdef HAVE_CTAPI
return ct_send_apdu (slot, apdu, apdulen, buffer, buflen); return ct_send_apdu (slot, apdu, apdulen, buffer, buflen);
#elif defined(HAVE_PCSC) #elif defined(HAVE_PCSC)
return -1; return SW_HOST_NO_DRIVER;
#else #else
return -1; return SW_HOST_NO_DRIVER;
#endif #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 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 RETBUF is not NULL, it will receive an allocated buffer with the
returned data. The length of that data will be put into 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. */ in case of errors. */
int int
apdu_send_le(int slot, int class, int ins, int p0, int p1, 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)) if (le != -1 && (le > 256 || le < 1))
return SW_WRONG_LENGTH; return SW_WRONG_LENGTH;
if ((!data && lc != -1) || (data && lc == -1)) if ((!data && lc != -1) || (data && lc == -1))
return -1; return SW_HOST_INV_VALUE;
apdulen = 0; apdulen = 0;
apdu[apdulen++] = class; 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", log_error ("apdu_send_simple(%d) failed: %s\n",
slot, error_string (slot, rc)); slot, error_string (slot, rc));
return -1; return SW_HOST_INCOMPLETE_CARD_RESPONSE;
} }
sw = (result[resultlen-2] << 8) | result[resultlen-1]; sw = (result[resultlen-2] << 8) | result[resultlen-1];
/* store away the returned data but strip the statusword. */ /* 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); *retbuf = xtrymalloc (resultlen? resultlen : 1);
if (!*retbuf) if (!*retbuf)
return -1; /* fixme: this is actually out of core. */ return SW_HOST_OUT_OF_CORE;
*retbuflen = resultlen; *retbuflen = resultlen;
memcpy (*retbuf, result, 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); *retbuf = p = xtrymalloc (bufsize);
if (!*retbuf) if (!*retbuf)
return -1; /* fixme: this is actually out of core. */ return SW_HOST_OUT_OF_CORE;
assert (resultlen < bufsize); assert (resultlen < bufsize);
memcpy (p, result, resultlen); memcpy (p, result, resultlen);
p += 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", log_error ("apdu_send_simple(%d) for get response failed: %s\n",
slot, error_string (slot, rc)); slot, error_string (slot, rc));
return -1; return SW_HOST_INCOMPLETE_CARD_RESPONSE;
} }
sw = (result[resultlen-2] << 8) | result[resultlen-1]; sw = (result[resultlen-2] << 8) | result[resultlen-1];
resultlen -= 2; resultlen -= 2;
@ -492,7 +492,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1,
bufsize += resultlen > 4096? resultlen: 4096; bufsize += resultlen > 4096? resultlen: 4096;
tmp = xtryrealloc (*retbuf, bufsize); tmp = xtryrealloc (*retbuf, bufsize);
if (!tmp) if (!tmp)
return -1; /* fixme: actually this is out of core */ return SW_HOST_OUT_OF_CORE;
p = tmp + (p - *retbuf); p = tmp + (p - *retbuf);
*retbuf = tmp; *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 caller is reponsible for releasing the buffer even in case of
errors. */ errors. */
int int
apdu_send(int slot, int class, int ins, int p0, int p1, apdu_send (int slot, int class, int ins, int p0, int p1,
int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
{ {
return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256, return apdu_send_le (slot, class, ins, p0, p1, lc, data, 256,
retbuf, retbuflen); retbuf, retbuflen);
@ -548,5 +548,9 @@ int
apdu_send_simple (int slot, int class, int ins, int p0, int p1, apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data) 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 #define APDU_H
/* ISO 7816 values for the statusword are defined here because they /* 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. */ API. */
enum { enum {
SW_MORE_DATA = 0x6100, /* Note: that the low byte must be SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
@ -32,19 +32,31 @@ enum {
SW_CHV_WRONG = 0x6982, SW_CHV_WRONG = 0x6982,
SW_CHV_BLOCKED = 0x6983, SW_CHV_BLOCKED = 0x6983,
SW_USE_CONDITIONS = 0x6985, SW_USE_CONDITIONS = 0x6985,
SW_NOT_SUPPORTED = 0x6a81,
SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */ SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
SW_REF_NOT_FOUND = 0x6a88, SW_REF_NOT_FOUND = 0x6a88,
SW_BAD_P0_P1 = 0x6b00, SW_BAD_P0_P1 = 0x6b00,
SW_INS_NOT_SUP = 0x6d00, SW_INS_NOT_SUP = 0x6d00,
SW_CLA_NOT_SUP = 0x6e00, 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); int apdu_open_reader (int port);
unsigned char *apdu_get_atr (int slot, size_t *atrlen); 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 apdu_send_simple (int slot, int class, int ins, int p0, int p1,
int lc, const char *data); int lc, const char *data);
int apdu_send (int slot, int class, int ins, int p0, int p1, 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*/ #endif /*APDU_H*/

View File

@ -44,6 +44,11 @@ struct app_ctx_s {
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); 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 (*decipher) (APP app, const char *keyidstr,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, void *pincb_arg,
@ -53,6 +58,10 @@ struct app_ctx_s {
const char *keynostr, unsigned int flags, const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); 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; } fnc;
@ -71,6 +80,11 @@ int app_sign (APP app, const char *keyidstr, int hashalgo,
void *pincb_arg, void *pincb_arg,
const void *indata, size_t indatalen, const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen ); 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 app_decipher (APP app, const char *keyidstr,
int (pincb)(void*, const char *, char **), int (pincb)(void*, const char *, char **),
void *pincb_arg, 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 app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
int (*pincb)(void*, const char *, char **), int (*pincb)(void*, const char *, char **),
void *pincb_arg); 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 --*/ /*-- app-openpgp.c --*/
int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen); 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; 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. */ /* Handle the GENKEY command. */
static int static int
@ -630,7 +724,7 @@ do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
} }
xfree (buffer); buffer = NULL; xfree (buffer); buffer = NULL;
#if 0 #if 1
log_info ("please wait while key is being generated ...\n"); log_info ("please wait while key is being generated ...\n");
start_at = time (NULL); start_at = time (NULL);
rc = iso7816_generate_keypair 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 card. This is allows for a meaningful error message in case
the key on the card has been replaced but the shadow information the key on the card has been replaced but the shadow information
known to gpg was not updated. If there is no fingerprint, gpg 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. */ verify-after-signing feature. */
if (fpr) if (fpr)
{ {
@ -883,6 +977,107 @@ do_sign (APP app, const char *keyidstr, int hashalgo,
return rc; 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 static int
do_decipher (APP app, const char *keyidstr, 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.setattr = do_setattr;
app->fnc.genkey = do_genkey; app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign; app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
app->fnc.decipher = do_decipher; app->fnc.decipher = do_decipher;
app->fnc.change_pin = do_change_pin;
} }
leave: leave:

View File

@ -28,6 +28,7 @@
#include "scdaemon.h" #include "scdaemon.h"
#include "app-common.h" #include "app-common.h"
#include "apdu.h" #include "apdu.h"
#include "iso7816.h"
/* The select the best fitting application and return a context. /* The select the best fitting application and return a context.
Returns NULL if no application was found or no card is present. */ 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; 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. /* 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 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; 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) if (*p)
return set_error (Parameter_Error, "invalid hexstring"); return set_error (Parameter_Error, "invalid hexstring");
if (!n)
return set_error (Parameter_Error, "no data given");
if ((n&1)) if ((n&1))
return set_error (Parameter_Error, "odd number of digits"); return set_error (Parameter_Error, "odd number of digits");
n /= 2; n /= 2;
@ -607,6 +609,52 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
return map_to_assuan_status (rc); 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> /* 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 }, { "READKEY", cmd_readkey },
{ "SETDATA", cmd_setdata }, { "SETDATA", cmd_setdata },
{ "PKSIGN", cmd_pksign }, { "PKSIGN", cmd_pksign },
{ "PKAUTH", cmd_pkauth },
{ "PKDECRYPT", cmd_pkdecrypt }, { "PKDECRYPT", cmd_pkdecrypt },
{ "INPUT", NULL }, { "INPUT", NULL },
{ "OUTPUT", NULL }, { "OUTPUT", NULL },
{ "SETATTR", cmd_setattr }, { "SETATTR", cmd_setattr },
{ "GENKEY", cmd_genkey }, { "GENKEY", cmd_genkey },
{ "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd },
{ NULL } { NULL }
}; };
int i, rc; int i, rc;

View File

@ -31,6 +31,8 @@
#define CMD_SELECT_FILE 0xA4 #define CMD_SELECT_FILE 0xA4
#define CMD_VERIFY 0x20 #define CMD_VERIFY 0x20
#define CMD_CHANGE_REFERENCE_DATA 0x24
#define CMD_RESET_RETRY_COUNTER 0x2C
#define CMD_GET_DATA 0xCA #define CMD_GET_DATA 0xCA
#define CMD_PUT_DATA 0xDA #define CMD_PUT_DATA 0xDA
#define CMD_PSO 0x2A #define CMD_PSO 0x2A
@ -38,6 +40,40 @@
#define CMD_GENERATE_KEYPAIR 0x47 #define CMD_GENERATE_KEYPAIR 0x47
#define CMD_GET_CHALLENGE 0x84 #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. /* This function is specialized version of the SELECT FILE command.
SLOT is the card and reader as created for example by SLOT is the card and reader as created for example by
apdu_open_reader (), AID is a buffer of size AIDLEN holding the 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 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 for okay or GNUPG error code. Note that ISO error codes are
internally mapped. */ internally mapped. */
int gpg_error_t
iso7816_select_application (int slot, const char *aid, size_t aidlen) iso7816_select_application (int slot, const char *aid, size_t aidlen)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid); sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
if (sw == SW_SUCCESS) return map_sw (sw);
return 0;
else
return -1; /* Fixme: we need a real error code. */
} }
/* Perform a VERIFY command on SLOT using the card holder verification /* Perform a VERIFY command on SLOT using the card holder verification
vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */ 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) iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
{ {
int sw; int sw;
sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
if (sw == SW_SUCCESS) return map_sw (sw);
return 0;
else
return -1; /* Fixme: we need a real error code. */
} }
/* 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 /* Perform a GET DATA command requesting TAG and storing the result in
a newly allocated buffer at the address passed by RESULT. Return a newly allocated buffer at the address passed by RESULT. Return
the length of this data at the address of RESULTLEN. */ the length of this data at the address of RESULTLEN. */
int gpg_error_t
iso7816_get_data (int slot, int tag, iso7816_get_data (int slot, int tag,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
@ -95,7 +172,7 @@ iso7816_get_data (int slot, int tag,
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
return 0; 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 /* Perform a PUT DATA command on card in SLOT. Write DATA of length
DATALEN to TAG. */ DATALEN to TAG. */
int gpg_error_t
iso7816_put_data (int slot, int tag, iso7816_put_data (int slot, int tag,
const unsigned char *data, size_t datalen) 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, sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
((tag >> 8) & 0xff), (tag & 0xff), ((tag >> 8) & 0xff), (tag & 0xff),
datalen, data); datalen, data);
if (sw == SW_SUCCESS) return map_sw (sw);
return 0;
else
return -1; /* Fixme: we need a real error code. */
} }
@ -124,7 +198,7 @@ iso7816_put_data (int slot, int tag,
success 0 is returned and the data is availavle in a newly success 0 is returned and the data is availavle in a newly
allocated buffer stored at RESULT with its length stored at allocated buffer stored at RESULT with its length stored at
RESULTLEN. */ RESULTLEN. */
int gpg_error_t
iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen, iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
@ -143,7 +217,7 @@ iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
return 0; 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 success 0 is returned and the plaintext is available in a newly
allocated buffer stored at RESULT with its length stored at allocated buffer stored at RESULT with its length stored at
RESULTLEN. */ RESULTLEN. */
int gpg_error_t
iso7816_decipher (int slot, const unsigned char *data, size_t datalen, iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
{ {
@ -181,14 +255,14 @@ iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
return 0; return 0;
} }
int gpg_error_t
iso7816_internal_authenticate (int slot, iso7816_internal_authenticate (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
@ -208,14 +282,14 @@ iso7816_internal_authenticate (int slot,
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
return 0; return 0;
} }
static int static gpg_error_t
generate_keypair (int slot, int readonly, generate_keypair (int slot, int readonly,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
@ -235,14 +309,14 @@ generate_keypair (int slot, int readonly,
xfree (*result); xfree (*result);
*result = NULL; *result = NULL;
*resultlen = 0; *resultlen = 0;
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
return 0; return 0;
} }
int gpg_error_t
iso7816_generate_keypair (int slot, iso7816_generate_keypair (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) 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, iso7816_read_public_key (int slot,
const unsigned char *data, size_t datalen, const unsigned char *data, size_t datalen,
unsigned char **result, size_t *resultlen) unsigned char **result, size_t *resultlen)
@ -261,12 +335,12 @@ iso7816_read_public_key (int slot,
int gpg_error_t
iso1816_get_challenge (int slot, int length, unsigned char *buffer) iso7816_get_challenge (int slot, int length, unsigned char *buffer)
{ {
int sw; int sw;
unsigned char *result; unsigned char *result;
size_t resultlen; size_t resultlen, n;
if (!buffer || length < 1) if (!buffer || length < 1)
return gpg_error (GPG_ERR_INV_VALUE); return gpg_error (GPG_ERR_INV_VALUE);
@ -274,26 +348,24 @@ iso1816_get_challenge (int slot, int length, unsigned char *buffer)
do do
{ {
result = NULL; result = NULL;
n = length > 254? 254 : length;
sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL, sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
length, n,
&result, &resultlen); &result, &resultlen);
if (sw != SW_SUCCESS) if (sw != SW_SUCCESS)
{ {
/* Make sure that pending buffers are released. */ /* Make sure that pending buffers are released. */
xfree (result); xfree (result);
return -1; /* FIXME: Map error codes. */ return map_sw (sw);
} }
if (resultlen > length) if (resultlen > n)
resultlen = length; resultlen = n;
memcpy (buffer, result, resultlen); memcpy (buffer, result, resultlen);
buffer += resultlen; buffer += resultlen;
length -= length; length -= resultlen;
xfree (result); xfree (result);
} }
while (length > 0); while (length > 0);
return 0; return 0;
} }

View File

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

View File

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