From b8becef1cf3d9bee4c1c514863015387b9c63f31 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 23 Jul 2003 07:13:05 +0000 Subject: [PATCH] * 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. --- scd/ChangeLog | 36 ++++++++ scd/apdu.c | 32 ++++--- scd/apdu.h | 19 +++- scd/app-common.h | 19 ++++ scd/app-openpgp.c | 201 ++++++++++++++++++++++++++++++++++++++++++- scd/app.c | 70 +++++++++++++++ scd/command.c | 130 ++++++++++++++++++++++++++++ scd/iso7816.c | 148 +++++++++++++++++++++++-------- scd/iso7816.h | 48 ++++++----- scd/sc-investigate.c | 47 ++++++++-- 10 files changed, 668 insertions(+), 82 deletions(-) diff --git a/scd/ChangeLog b/scd/ChangeLog index b5c49e96a..6657806de 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,39 @@ +2003-07-23 Werner Koch + + * 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 + + * 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 + + * 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 + + * 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 * Makefile.am: Add sc-copykeys program. diff --git a/scd/apdu.c b/scd/apdu.c index 63dfa0684..773225415 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -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); } + + + + diff --git a/scd/apdu.h b/scd/apdu.h index 47fd1a8f6..44166a3fe 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -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*/ + + + diff --git a/scd/app-common.h b/scd/app-common.h index 1527338dd..108d5ecdd 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -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); diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 1f9ec56f0..bc93e4b09 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -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: diff --git a/scd/app.c b/scd/app.c index 7d143ad13..7a85df336 100644 --- a/scd/app.c +++ b/scd/app.c @@ -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; +} + + + + + + diff --git a/scd/command.c b/scd/command.c index ac6ddb412..c53af84f9 100644 --- a/scd/command.c +++ b/scd/command.c @@ -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 + + */ +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 */ @@ -746,6 +794,85 @@ cmd_genkey (ASSUAN_CONTEXT ctx, char *line) } +/* RANDOM + + 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] + + 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; diff --git a/scd/iso7816.c b/scd/iso7816.c index de3ada7c6..8903d8a5c 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -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; } - - - diff --git a/scd/iso7816.h b/scd/iso7816.h index 660699416..d7e77a101 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -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*/ diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c index e7fd6dddc..e8f0eb83c 100644 --- a/scd/sc-investigate.c +++ b/scd/sc-investigate.c @@ -23,6 +23,7 @@ #include #include #include +#include #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; }