diff --git a/agent/pksign.c b/agent/pksign.c index 817ce6ea5..2973fb837 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -75,7 +75,7 @@ do_encode_md (const byte * md, size_t mdlen, int algo, gcry_sexp_t * r_hash, } -/* Special version of do_encode_md to take care of pckcs#1 padding. +/* Special version of do_encode_md to take care of pkcs#1 padding. For TLS-MD5SHA1 we need to do the padding ourself as Libgrypt does not know about this special scheme. Fixme: We should have a pkcs1-only-padding flag for Libgcrypt. */ diff --git a/doc/gpg.texi b/doc/gpg.texi index 2ea851b5b..4f347d106 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -2709,6 +2709,9 @@ make a clear text signature @item gpg -sb @code{file} make a detached signature +@item gpg -u 0x12345678 -sb @code{file} +make a detached signature with the key 0x12345678 + @item gpg --list-keys @code{user_ID} show keys diff --git a/g10/ChangeLog b/g10/ChangeLog index 3478257d5..12630794d 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,14 @@ +2008-09-16 Werner Koch + + * card-util.c (fpr_is_ff): New. + (card_status): Do not print general key info for an all-ff fpr. + (change_login, change_private_do): Factor common code out to ... + (get_data_from_file): .. new. + (change_cert): New. + (card_edit): Add command "writecert". + * call-agent.c (writecert_parm_s): New. + (inq_writecert_parms, agent_scd_writecert): New. + 2008-09-04 David Shaw * keyserver.c (keyserver_import_cert): Allow keyserver URLs in diff --git a/g10/call-agent.c b/g10/call-agent.c index fa68e6149..9767f040f 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -53,6 +53,13 @@ struct cipher_parm_s size_t ciphertextlen; }; +struct writecert_parm_s +{ + assuan_context_t ctx; + const unsigned char *certdata; + size_t certdatalen; +}; + struct writekey_parm_s { assuan_context_t ctx; @@ -444,6 +451,56 @@ agent_scd_setattr (const char *name, } + +/* Handle a CERTDATA inquiry. Note, we only send the data, + assuan_transact takes care of flushing and writing the END + command. */ +static int +inq_writecert_parms (void *opaque, const char *line) +{ + int rc; + struct writecert_parm_s *parm = opaque; + + if (!strncmp (line, "CERTDATA", 8) && (line[8]==' '||!line[8])) + { + rc = assuan_send_data (parm->ctx, parm->certdata, parm->certdatalen); + } + else + rc = default_inq_cb (opaque, line); + + return rc; +} + + +/* Send a WRITECERT command to the SCdaemon. */ +int +agent_scd_writecert (const char *certidstr, + const unsigned char *certdata, size_t certdatalen) +{ + int rc; + char line[ASSUAN_LINELENGTH]; + struct writecert_parm_s parms; + + rc = start_agent (); + if (rc) + return rc; + + memset (&parms, 0, sizeof parms); + + snprintf (line, DIM(line)-1, "SCD WRITECERT %s", certidstr); + line[DIM(line)-1] = 0; + parms.ctx = agent_ctx; + parms.certdata = certdata; + parms.certdatalen = certdatalen; + + rc = assuan_transact (agent_ctx, line, NULL, NULL, + inq_writecert_parms, &parms, NULL, NULL); + + return rc; +} + + + /* Handle a KEYDATA inquiry. Note, we only send the data, assuan_transact takes care of flushing and writing the end */ diff --git a/g10/call-agent.h b/g10/call-agent.h index b3e8ae2b6..63b460a90 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -76,6 +76,10 @@ int agent_scd_setattr (const char *name, const unsigned char *value, size_t valuelen, const char *serialno); +/* Send a WRITECERT command to the SCdaemon. */ +int agent_scd_writecert (const char *certidstr, + const unsigned char *certdata, size_t certdatalen); + /* Send a WRITEKEY command to the SCdaemon. */ int agent_scd_writekey (int keyno, const char *serialno, const unsigned char *keydata, size_t keydatalen); diff --git a/g10/card-util.c b/g10/card-util.c index 2778bb871..2d00bb3ac 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -288,6 +288,18 @@ fpr_is_zero (const char *fpr) } +/* Return true if the SHA1 fingerprint FPR consists only of 0xFF. */ +static int +fpr_is_ff (const char *fpr) +{ + int i; + + for (i=0; i < 20 && fpr[i] == '\xff'; i++) + ; + return (i == 20); +} + + /* Print all available information about the current card. */ void card_status (FILE *fp, char *serialno, size_t serialnobuflen) @@ -467,7 +479,10 @@ card_status (FILE *fp, char *serialno, size_t serialnobuflen) thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 : info.fpr3valid? info.fpr3 : NULL); - if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20)) + /* If the fingerprint is all 0xff, the key has no asssociated + OpenPGP certificate. */ + if ( thefpr && !fpr_is_ff (thefpr) + && !get_pubkey_byfprint (pk, thefpr, 20)) { KBNODE keyblock = NULL; @@ -655,6 +670,58 @@ fetch_url(void) } +/* Read data from file FNAME up to MAXLEN characters. On error return + -1 and store NULl at R_BUFFER; on success return the number of + bytes read and store the address of a newly allocated buffer at + R_BUFFER. */ +static int +get_data_from_file (const char *fname, size_t maxlen, char **r_buffer) +{ + FILE *fp; + char *data; + int n; + + *r_buffer = NULL; + + fp = fopen (fname, "rb"); +#if GNUPG_MAJOR_VERSION == 1 + if (fp && is_secured_file (fileno (fp))) + { + fclose (fp); + fp = NULL; + errno = EPERM; + } +#endif + if (!fp) + { + tty_printf (_("can't open `%s': %s\n"), fname, strerror (errno)); + return -1; + } + + data = xtrymalloc (maxlen? maxlen:1); + if (!data) + { + tty_printf (_("error allocating enough memory: %s\n"), strerror (errno)); + fclose (fp); + return -1; + } + + if (maxlen) + n = fread (data, 1, maxlen, fp); + else + n = 0; + fclose (fp); + if (n < 0) + { + tty_printf (_("error reading `%s': %s\n"), fname, strerror (errno)); + xfree (data); + return -1; + } + *r_buffer = data; + return n; +} + + static int change_login (const char *args) { @@ -664,34 +731,11 @@ change_login (const char *args) if (args && *args == '<') /* Read it from a file */ { - FILE *fp; - for (args++; spacep (args); args++) ; - fp = fopen (args, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif - if (!fp) - { - tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); - return -1; - } - - data = xmalloc (254); - n = fread (data, 1, 254, fp); - fclose (fp); + n = get_data_from_file (args, 254, &data); if (n < 0) - { - tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); - xfree (data); - return -1; - } + return -1; } else { @@ -732,35 +776,11 @@ change_private_do (const char *args, int nr) if (args && (args = strchr (args, '<'))) /* Read it from a file */ { - FILE *fp; - - /* Fixme: Factor this duplicated code out. */ for (args++; spacep (args); args++) ; - fp = fopen (args, "rb"); -#if GNUPG_MAJOR_VERSION == 1 - if (fp && is_secured_file (fileno (fp))) - { - fclose (fp); - fp = NULL; - errno = EPERM; - } -#endif - if (!fp) - { - tty_printf (_("can't open `%s': %s\n"), args, strerror (errno)); - return -1; - } - - data = xmalloc (254); - n = fread (data, 1, 254, fp); - fclose (fp); + n = get_data_from_file (args, 254, &data); if (n < 0) - { - tty_printf (_("error reading `%s': %s\n"), args, strerror (errno)); - xfree (data); - return -1; - } + return -1; } else { @@ -788,6 +808,36 @@ change_private_do (const char *args, int nr) return rc; } + +static int +change_cert (const char *args) +{ + char *data; + int n; + int rc; + + if (args && *args == '<') /* Read it from a file */ + { + for (args++; spacep (args); args++) + ; + n = get_data_from_file (args, 16384, &data); + if (n < 0) + return -1; + } + else + { + tty_printf ("usage error: redirectrion to file required\n"); + return -1; + } + + rc = agent_scd_writecert ("OPENPGP.3", data, n); + if (rc) + log_error ("error writing certificate to card: %s\n", gpg_strerror (rc)); + xfree (data); + return rc; +} + + static int change_lang (void) { @@ -1294,7 +1344,7 @@ enum cmdids cmdNOP = 0, cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY, cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR, - cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, + cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT, cmdINVCMD }; @@ -1325,8 +1375,9 @@ static struct { "generate", cmdGENERATE, 1, N_("generate new keys")}, { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")}, { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")}, - /* Note, that we do not announce this command yet. */ + /* Note, that we do not announce these command yet. */ { "privatedo", cmdPRIVATEDO, 0, NULL }, + { "writecert", cmdWRITECERT, 1, NULL }, { NULL, cmdINVCMD, 0, NULL } }; @@ -1401,6 +1452,7 @@ card_edit (strlist_t commands) { int arg_number; const char *arg_string = ""; + const char *arg_rest = ""; char *p; int i; int cmd_admin_only; @@ -1469,6 +1521,11 @@ card_edit (strlist_t commands) trim_spaces (p); arg_number = atoi(p); arg_string = p; + arg_rest = p; + while (digitp (arg_rest)) + arg_rest++; + while (spacep (arg_rest)) + arg_rest++; } for (i=0; cmds[i].name; i++ ) @@ -1567,6 +1624,13 @@ card_edit (strlist_t commands) change_private_do (arg_string, arg_number); break; + case cmdWRITECERT: + if ( arg_number != 3 ) + tty_printf ("usage: writecert 3 < FILE\n"); + else + change_cert (arg_rest); + break; + case cmdFORCESIG: toggle_forcesig (); break; diff --git a/scd/ChangeLog b/scd/ChangeLog index ee340540f..d9ad2bfb0 100644 --- a/scd/ChangeLog +++ b/scd/ChangeLog @@ -1,3 +1,29 @@ +2008-09-23 Werner Koch + + * app-openpgp.c (do_setattr): Use command chaining for long + values. + * iso7816.c (iso7816_put_data): Add arg EXTENDED_MODE. Change all + callers. + * apdu.c (apdu_send_simple): Add arg EXTENDED_MODE. Change all + callers. + (send_le): Implement command chaining. + * ccid-driver.c (ccid_transceive_apdu_level): Increase allowed + APDU size. + * apdu.h: Add new SW_ codes. + +2008-09-16 Werner Koch + + * command.c (cmd_writecert): New. + (register_commands): Register it. + * app-common.h (app_ctx_s): Add member WRITECERT. + * app.c (app_writecert): New. + * app-openpgp.c (do_writecert): New. + (parse_historical): New. + (show_extcap): New. + (dump_all_do): Print only the length of longs DOs. + * command.c (cmd_writekey, cmd_apdu, cmd_pksign) + (cmd_passwd): Replace open coding by skip_options. + 2008-08-30 Moritz * scdaemon.c (main): Use estream_asprintf instead of asprintf. diff --git a/scd/apdu.c b/scd/apdu.c index 2aa584bad..171c7a63a 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -1,5 +1,5 @@ /* apdu.c - ISO 7816 APDU functions and low level I/O - * Copyright (C) 2003, 2004 Free Software Foundation, Inc. + * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -1182,7 +1182,7 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, err = SW_HOST_INV_VALUE; } /* We need to read any rest of the response, to keep the - protocol runnng. */ + protocol running. */ while (full_len) { unsigned char dummybuf[128]; @@ -2587,20 +2587,29 @@ send_apdu (int slot, unsigned char *apdu, size_t apdulen, if (reader_table[slot].send_apdu_reader) return reader_table[slot].send_apdu_reader (slot, apdu, apdulen, - buffer, buflen, pininfo); + buffer, buflen, + pininfo); else return SW_HOST_NOT_SUPPORTED; } -/* Core APDU trabceiver function. Parameters are described at +/* Core APDU tranceiver function. Parameters are described at apdu_send_le with the exception of PININFO which indicates keypad - related operations if not NULL. */ + related operations if not NULL. If EXTENDED_MODE is not NULL + command chaining or extended length will be used according to these + values: + n < 0 := Use command chaining without the data part limited to -n + in each chunk. If -1 is used a default value is used. + n == 1 := Use extended length for input and output with out a + length limit. + n > 1 := Use extended length with up to N bytes. +*/ static int send_le (int slot, int class, int ins, int p0, int p1, int lc, const char *data, int le, unsigned char **retbuf, size_t *retbuflen, - struct pininfo_s *pininfo) + struct pininfo_s *pininfo, int extended_mode) { #define RESULTLEN 258 unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in @@ -2611,16 +2620,37 @@ send_le (int slot, int class, int ins, int p0, int p1, int sw; long rc; /* We need a long here due to PC/SC. */ int did_exact_length_hack = 0; + int use_chaining = 0; + int lc_chunk; if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used ) return SW_HOST_NO_DRIVER; if (DBG_CARD_IO) - log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n", - class, ins, p0, p1, lc, le); + log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d em=%d\n", + class, ins, p0, p1, lc, le, extended_mode); if (lc != -1 && (lc > 255 || lc < 0)) - return SW_WRONG_LENGTH; + { + /* Data does not fit into an APDU. What we do now dependes on + the EXTENDED_MODE parameter. */ + if (!extended_mode) + return SW_WRONG_LENGTH; /* No way. to send such an APDU. */ + else if (extended_mode > 0) + return SW_HOST_NOT_SUPPORTED; /* FIXME. */ + else if (extended_mode < 0) + { + /* Send APDU using chaining mode. */ + if (lc > 16384) + return SW_WRONG_LENGTH; /* Sanity check. */ + if ((class&0xf0) != 0) + return SW_HOST_INV_VALUE; /* Upper 4 bits need to be 0. */ + use_chaining = extended_mode == -1? 255 : -extended_mode; + use_chaining &= 0xff; + } + else + return SW_HOST_INV_VALUE; + } if (le != -1 && (le > 256 || le < 0)) return SW_WRONG_LENGTH; if ((!data && lc != -1) || (data && lc == -1)) @@ -2629,43 +2659,61 @@ send_le (int slot, int class, int ins, int p0, int p1, if ((sw = lock_slot (slot))) return sw; - apdulen = 0; - apdu[apdulen++] = class; - apdu[apdulen++] = ins; - apdu[apdulen++] = p0; - apdu[apdulen++] = p1; - if (lc != -1) + do { - apdu[apdulen++] = lc; - memcpy (apdu+apdulen, data, lc); - apdulen += lc; - /* T=0 does not allow the use of Lc together with Le; thus - disable Le in this case. */ - if (reader_table[slot].is_t0) - le = -1; - } - if (le != -1) - apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */ - assert (sizeof (apdu) >= apdulen); - /* As safeguard don't pass any garbage from the stack to the driver. */ - memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); - exact_length_hack: - resultlen = RESULTLEN; - rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); - if (rc || resultlen < 2) - { - log_error ("apdu_send_simple(%d) failed: %s\n", - slot, apdu_strerror (rc)); - unlock_slot (slot); - return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; - } - sw = (result[resultlen-2] << 8) | result[resultlen-1]; - if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw)) - { - apdu[apdulen-1] = (sw & 0x00ff); - did_exact_length_hack = 1; - goto exact_length_hack; + apdulen = 0; + apdu[apdulen] = class; + if (use_chaining && lc > 255) + { + apdu[apdulen] |= 0x10; + assert (use_chaining < 256); + lc_chunk = use_chaining; + lc -= use_chaining; + } + else + { + use_chaining = 0; + lc_chunk = lc; + } + apdulen++; + apdu[apdulen++] = ins; + apdu[apdulen++] = p0; + apdu[apdulen++] = p1; + if (lc_chunk != -1) + { + apdu[apdulen++] = lc_chunk; + memcpy (apdu+apdulen, data, lc_chunk); + data += lc_chunk; + apdulen += lc_chunk; + /* T=0 does not allow the use of Lc together with Le; thus + disable Le in this case. */ + if (reader_table[slot].is_t0) + le = -1; + } + if (le != -1) + apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */ + /* As safeguard don't pass any garbage from the stack to the driver. */ + assert (sizeof (apdu) >= apdulen); + memset (apdu+apdulen, 0, sizeof (apdu) - apdulen); + exact_length_hack: + resultlen = RESULTLEN; + rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo); + if (rc || resultlen < 2) + { + log_error ("apdu_send_simple(%d) failed: %s\n", + slot, apdu_strerror (rc)); + unlock_slot (slot); + return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE; + } + sw = (result[resultlen-2] << 8) | result[resultlen-1]; + if (!did_exact_length_hack && SW_EXACT_LENGTH_P (sw)) + { + apdu[apdulen-1] = (sw & 0x00ff); + did_exact_length_hack = 1; + goto exact_length_hack; + } } + while (use_chaining && sw == SW_SUCCESS); /* Store away the returned data but strip the statusword. */ resultlen -= 2; @@ -2808,7 +2856,7 @@ apdu_send_le(int slot, int class, int ins, int p0, int p1, return send_le (slot, class, ins, p0, p1, lc, data, le, retbuf, retbuflen, - NULL); + NULL, 0); } @@ -2826,7 +2874,7 @@ apdu_send (int slot, int class, int ins, int p0, int p1, int lc, const char *data, unsigned char **retbuf, size_t *retbuflen) { return send_le (slot, class, ins, p0, p1, lc, data, 256, - retbuf, retbuflen, NULL); + retbuf, retbuflen, NULL, 0); } /* Send an APDU to the card in SLOT. The APDU is created from all @@ -2836,10 +2884,12 @@ apdu_send (int slot, int class, int ins, int p0, int p1, for an invalid SLOT or other non card related error. No data will be returned. */ int -apdu_send_simple (int slot, int class, int ins, int p0, int p1, +apdu_send_simple (int slot, int extended_mode, + int class, int ins, int p0, int p1, int lc, const char *data) { - return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL); + return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL, + extended_mode); } @@ -2857,13 +2907,13 @@ apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, pininfo.maxlen = pinlen_max; pininfo.padlen = pin_padlen; return send_le (slot, class, ins, p0, p1, lc, data, -1, - NULL, NULL, &pininfo); + NULL, NULL, &pininfo, 0); } /* This is a more generic version of the apdu sending routine. It takes an already formatted APDU in APDUDATA or length APDUDATALEN - and returns the with the APDU including the status word. With + and returns the with an APDU including the status word. With HANDLE_MORE set to true this function will handle the MORE DATA status and return all APDUs concatenated with one status word at the end. The function does not return a regular status word but 0 diff --git a/scd/apdu.h b/scd/apdu.h index 92d073897..361dfdead 100644 --- a/scd/apdu.h +++ b/scd/apdu.h @@ -1,5 +1,5 @@ /* apdu.h - ISO 7816 APDU functions and low level I/O - * Copyright (C) 2003 Free Software Foundation, Inc. + * Copyright (C) 2003, 2008 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -29,8 +29,11 @@ enum { SW_MORE_DATA = 0x6100, /* Note: that the low byte must be masked of.*/ SW_EOF_REACHED = 0x6282, + SW_TERM_STATE = 0x6285, /* Selected file is in termination state. */ SW_EEPROM_FAILURE = 0x6581, SW_WRONG_LENGTH = 0x6700, + SW_SM_NOT_SUP = 0x6882, /* Secure Messaging is not supported. */ + SW_CC_NOT_SUP = 0x6884, /* Command Chaining is not supported. */ SW_CHV_WRONG = 0x6982, SW_CHV_BLOCKED = 0x6983, SW_USE_CONDITIONS = 0x6985, @@ -97,7 +100,8 @@ int apdu_get_status (int slot, int hang, unsigned int *status, unsigned int *changed); int apdu_check_keypad (int slot, int command, int pin_mode, int pinlen_min, int pinlen_max, int pin_padlen); -int apdu_send_simple (int slot, int class, int ins, int p0, int p1, +int apdu_send_simple (int slot, int extended_mode, + int class, int ins, int p0, int p1, int lc, const char *data); int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1, int lc, const char *data, diff --git a/scd/app-common.h b/scd/app-common.h index dc1581e50..96fbb92aa 100644 --- a/scd/app-common.h +++ b/scd/app-common.h @@ -1,5 +1,5 @@ /* app-common.h - Common declarations for all card applications - * Copyright (C) 2003, 2005 Free Software Foundation, Inc. + * Copyright (C) 2003, 2005, 2008 Free Software Foundation, Inc. * * This file is part of GnuPG. * @@ -94,8 +94,13 @@ struct app_ctx_s { void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen); + gpg_error_t (*writecert) (app_t app, ctrl_t ctrl, + const char *certid, + gpg_error_t (*pincb)(void*,const char *,char **), + void *pincb_arg, + const unsigned char *data, size_t datalen); gpg_error_t (*writekey) (app_t app, ctrl_t ctrl, - const char *certid, unsigned int flags, + const char *keyid, unsigned int flags, gpg_error_t (*pincb)(void*,const char *,char **), void *pincb_arg, const unsigned char *pk, size_t pklen); @@ -165,6 +170,11 @@ gpg_error_t app_decipher (app_t app, const char *keyidstr, void *pincb_arg, const void *indata, size_t indatalen, unsigned char **outdata, size_t *outdatalen ); +gpg_error_t app_writecert (app_t app, ctrl_t ctrl, + const char *certidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *keydata, size_t keydatalen); gpg_error_t app_writekey (app_t app, ctrl_t ctrl, const char *keyidstr, unsigned int flags, gpg_error_t (*pincb)(void*, const char *, char **), diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c index 1750668f4..e2140f2c6 100644 --- a/scd/app-openpgp.c +++ b/scd/app-openpgp.c @@ -63,6 +63,7 @@ static struct { } data_objects[] = { { 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" }, { 0x5F50, 0, 0, 0, 0, 0, 0, "URL" }, + { 0x5F52, 0, 0, 1, 0, 0, 0, "Historical Bytes" }, { 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"}, { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" }, { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" }, @@ -118,7 +119,16 @@ struct app_local_s { implicitly available. */ } pk[3]; - /* Keep track of card capabilities. */ + unsigned char status_indicator; /* The card status indicator. */ + + /* Keep track of the ISO card capabilities. */ + struct + { + unsigned int cmd_chaining:1; /* Command chaining is supported. */ + unsigned int ext_lc_le:1; /* Extended Lc and Le are supported. */ + } cardcap; + + /* Keep track of extended card capabilities. */ struct { unsigned int is_v2:1; /* This is a v2.0 compatible card. */ @@ -126,7 +136,12 @@ struct app_local_s { unsigned int key_import:1; unsigned int change_force_chv:1; unsigned int private_dos:1; + unsigned int sm_supported:1; /* Secure Messaging is supported. */ + unsigned int sm_aes128:1; /* Use AES-128 for SM. */ unsigned int max_certlen_3:16; + unsigned int max_get_challenge:16; /* Maximum size for get_challenge. */ + unsigned int max_cmd_data:16; /* Maximum data size for a command. */ + unsigned int max_rsp_data:16; /* Maximum size of a response. */ } extcap; /* Flags used to control the application. */ @@ -451,7 +466,10 @@ dump_all_do (int slot) if (data_objects[j].binary) { log_info ("DO `%s': ", data_objects[j].desc); - log_printhex ("", value, valuelen); + if (valuelen > 200) + log_info ("[%u]\n", (unsigned int)valuelen); + else + log_printhex ("", value, valuelen); } else log_info ("DO `%s': `%.*s'\n", @@ -596,8 +614,9 @@ store_fpr (int slot, int keynumber, u32 timestamp, xfree (buffer); - rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6) - + keynumber, fpr, 20); + rc = iso7816_put_data (slot, 0, + (card_version > 0x0007? 0xC7 : 0xC6) + + keynumber, fpr, 20); if (rc) log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc)); @@ -610,7 +629,7 @@ store_fpr (int slot, int keynumber, u32 timestamp, buf[2] = timestamp >> 8; buf[3] = timestamp; - rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4); + rc = iso7816_put_data (slot, 0, 0xCE + keynumber, buf, 4); if (rc) log_error (_("failed to store the creation date: %s\n"), gpg_strerror (rc)); @@ -1278,10 +1297,10 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen) #endif } -/* Read the statdard certificate of an OpenPGP v2 card. It is +/* Read the standard certificate of an OpenPGP v2 card. It is returned in a freshly allocated buffer with that address stored at CERT and the length of the certificate stored at CERTLEN. CERTID - needs to be set to "OpenPGP.3". */ + needs to be set to "OPENPGP.3". */ static gpg_error_t do_readcert (app_t app, const char *certid, unsigned char **cert, size_t *certlen) @@ -1296,10 +1315,10 @@ do_readcert (app_t app, const char *certid, *certlen = 0; if (strcmp (certid, "OPENPGP.3")) return gpg_error (GPG_ERR_INV_ID); - if (app->app_local->extcap.is_v2) + if (!app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_FOUND); - relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); + relptr = get_one_do (app, 0x7F21, &buffer, &buflen, NULL); if (!relptr) return gpg_error (GPG_ERR_NOT_FOUND); @@ -1649,15 +1668,18 @@ do_setattr (app_t app, const char *name, { "PRIVATE-DO-3", 0x0103, 2 }, { "PRIVATE-DO-4", 0x0104, 3 }, { "CERT-3", 0x7F21, 3, 0, 1 }, + { "SM-KEY-ENC", 0x00D1, 3, 0, 1 }, + { "SM-KEY-MAC", 0x00D2, 3, 0, 1 }, + { "PW-RESET-CODE",0x00D3, 3, 0, 1 }, { NULL, 0 } }; - + int exmode; for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++) ; if (!table[idx].name) return gpg_error (GPG_ERR_INV_NAME); - if (table[idx].need_v2) + if (table[idx].need_v2 && !app->app_local->extcap.is_v2) return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported. */ switch (table[idx].need_chv) @@ -1678,7 +1700,12 @@ do_setattr (app_t app, const char *name, will reread the data from the card and thus get synced in case of errors (e.g. data truncated by the card). */ flush_cache_item (app, table[idx].tag); - rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen); + /* For command chaining we use a value of 254 for this card. */ + if (app->app_local->cardcap.cmd_chaining && valuelen > 254) + exmode = -254; + else + exmode = 0; + rc = iso7816_put_data (app->slot, exmode, table[idx].tag, value, valuelen); if (rc) log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc)); @@ -1691,6 +1718,34 @@ do_setattr (app_t app, const char *name, } +/* Handle the WRITECERT command for OpenPGP. This rites the standard + certifciate to the card; CERTID needs to be set to "OPENPGP.3". + PINCB and PINCB_ARG are the usual arguments for the pinentry + callback. */ +static gpg_error_t +do_writecert (app_t app, ctrl_t ctrl, + const char *certidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *certdata, size_t certdatalen) +{ +#if GNUPG_MAJOR_VERSION > 1 + if (strcmp (certidstr, "OPENPGP.3")) + return gpg_error (GPG_ERR_INV_ID); + if (!certdata || !certdatalen) + return gpg_error (GPG_ERR_INV_ARG); + if (!app->app_local->extcap.is_v2) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (certdatalen > app->app_local->extcap.max_certlen_3) + return gpg_error (GPG_ERR_TOO_LARGE); + return do_setattr (app, "CERT-3", pincb, pincb_arg, certdata, certdatalen); +#else + return gpg_error (GPG_ERR_NOT_IMPLEMENTED); +#endif +} + + + /* Handle the PASSWD command. */ static gpg_error_t do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, @@ -2074,7 +2129,7 @@ do_writekey (app_t app, ctrl_t ctrl, goto leave; /* Store the key. */ - err = iso7816_put_data (app->slot, + err = iso7816_put_data (app->slot, 0, (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno, template, template_len); if (err) @@ -2711,6 +2766,80 @@ do_check_pin (app_t app, const char *keyidstr, } +/* Show information about card capabilities. */ +static void +show_caps (struct app_local_s *s) +{ + log_info ("Version-2 ......: %s\n", s->extcap.is_v2? "yes":"no"); + log_info ("Get-Challenge ..: %s", s->extcap.get_challenge? "yes":"no"); + if (s->extcap.get_challenge) + log_printf (" (%u bytes max)", s->extcap.max_get_challenge); + log_info ("Key-Import .....: %s\n", s->extcap.key_import? "yes":"no"); + log_info ("Change-Force-PW1: %s\n", s->extcap.change_force_chv? "yes":"no"); + log_info ("Private-DOs ....: %s\n", s->extcap.private_dos? "yes":"no"); + log_info ("SM-Support .....: %s", s->extcap.sm_supported? "yes":"no"); + if (s->extcap.sm_supported) + log_printf (" (%s)", s->extcap.sm_aes128? "AES-128":"3DES"); + log_info ("Max-Cert3-Len ..: %u\n", s->extcap.max_certlen_3); + log_info ("Max-Cmd-Data ...: %u\n", s->extcap.max_cmd_data); + log_info ("Max-Rsp-Data ...: %u\n", s->extcap.max_rsp_data); + log_info ("Cmd-Chaining ...: %s\n", s->cardcap.cmd_chaining?"yes":"no"); + log_info ("Ext-Lc-Le ......: %s\n", s->cardcap.ext_lc_le?"yes":"no"); + log_info ("Status Indicator: %02X\n", s->status_indicator); + + log_info ("GnuPG-No-Sync ..: %s\n", s->flags.no_sync? "yes":"no"); + log_info ("GnuPG-Def-PW2 ..: %s\n", s->flags.def_chv2? "yes":"no"); +} + + +/* Parse the historical bytes in BUFFER of BUFLEN and store them in + APPLOC. */ +static void +parse_historical (struct app_local_s *apploc, + const unsigned char * buffer, size_t buflen) +{ + /* Example buffer: 00 31 C5 73 C0 01 80 00 90 00 */ + if (buflen < 4) + { + log_error ("warning: historical bytes are too short\n"); + return; /* Too short. */ + } + if (*buffer) + { + log_error ("warning: bad category indicator in historical bytes\n"); + return; + } + + /* Skip category indicator. */ + buffer++; + buflen--; + + /* Get the status indicator. */ + apploc->status_indicator = buffer[buflen-3]; + buflen -= 3; + + /* Parse the compact TLV. */ + while (buflen) + { + unsigned int tag = (*buffer & 0xf0) >> 4; + unsigned int len = (*buffer & 0x0f); + if (len+1 > buflen) + { + log_error ("warning: bad Compact-TLV in historical bytes\n"); + return; /* Error. */ + } + buffer++; + buflen--; + if (tag == 7 && len == 3) + { + /* Card capabilities. */ + apploc->cardcap.cmd_chaining = !!(buffer[2] & 0x80); + apploc->cardcap.ext_lc_le = !!(buffer[2] & 0x40); + } + buffer += len; + buflen -= len; + } +} /* Select the OpenPGP application on the card in SLOT. This function @@ -2771,6 +2900,22 @@ app_select_openpgp (app_t app) if (app->card_version >= 0x0200) app->app_local->extcap.is_v2 = 1; + + /* Read the historical bytes. */ + relptr = get_one_do (app, 0x5f52, &buffer, &buflen, NULL); + if (relptr) + { + if (opt.verbose) + { + log_info ("Historical Bytes: "); + log_printhex ("", buffer, buflen); + } + parse_historical (app->app_local, buffer, buflen); + xfree (relptr); + } + + + /* Read the force-chv1 flag. */ relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL); if (!relptr) { @@ -2781,6 +2926,7 @@ app_select_openpgp (app_t app) app->force_chv1 = (buflen && *buffer == 0); xfree (relptr); + /* Read the extended capabilities. */ relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL); if (!relptr) { @@ -2790,6 +2936,7 @@ app_select_openpgp (app_t app) } if (buflen) { + app->app_local->extcap.sm_supported = !!(*buffer & 0x80); app->app_local->extcap.get_challenge = !!(*buffer & 0x40); app->app_local->extcap.key_import = !!(*buffer & 0x20); app->app_local->extcap.change_force_chv = !!(*buffer & 0x10); @@ -2798,7 +2945,12 @@ app_select_openpgp (app_t app) if (buflen >= 10) { /* Available with v2 cards. */ + app->app_local->extcap.sm_aes128 = (buffer[1] == 1); + app->app_local->extcap.max_get_challenge + = (buffer[2] << 8 | buffer[3]); app->app_local->extcap.max_certlen_3 = (buffer[4] << 8 | buffer[5]); + app->app_local->extcap.max_cmd_data = (buffer[6] << 8 | buffer[7]); + app->app_local->extcap.max_rsp_data = (buffer[8] << 8 | buffer[9]); } xfree (relptr); @@ -2809,6 +2961,9 @@ app_select_openpgp (app_t app) parse_login_data (app); + if (opt.verbose) + show_caps (app->app_local); + if (opt.verbose > 1) dump_all_do (slot); @@ -2818,6 +2973,7 @@ app_select_openpgp (app_t app) app->fnc.readkey = do_readkey; app->fnc.getattr = do_getattr; app->fnc.setattr = do_setattr; + app->fnc.writecert = do_writecert; app->fnc.writekey = do_writekey; app->fnc.genkey = do_genkey; app->fnc.sign = do_sign; diff --git a/scd/app.c b/scd/app.c index 8cb066f30..d2732beb6 100644 --- a/scd/app.c +++ b/scd/app.c @@ -240,8 +240,8 @@ select_application (ctrl_t ctrl, int slot, const char *name, app_t *r_app) /* If we don't have an app, check whether we have a saved application for that slot. This is useful so that a card does - not get reset even if only one session is using the card - so the - PIN cache and other cached data are preserved. */ + not get reset even if only one session is using the card - this + way the PIN cache and other cached data are preserved. */ if (!app && lock_table[slot].initialized && lock_table[slot].last_app) { app = lock_table[slot].last_app; @@ -734,6 +734,34 @@ app_decipher (app_t app, const char *keyidstr, } +/* Perform the WRITECERT operation. */ +gpg_error_t +app_writecert (app_t app, ctrl_t ctrl, + const char *certidstr, + gpg_error_t (*pincb)(void*, const char *, char **), + void *pincb_arg, + const unsigned char *data, size_t datalen) +{ + gpg_error_t err; + + if (!app || !certidstr || !*certidstr || !pincb) + return gpg_error (GPG_ERR_INV_VALUE); + if (!app->initialized) + return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED); + if (!app->fnc.writecert) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + err = lock_reader (app->slot); + if (err) + return err; + err = app->fnc.writecert (app, ctrl, certidstr, + pincb, pincb_arg, data, datalen); + unlock_reader (app->slot); + if (opt.verbose) + log_info ("operation writecert result: %s\n", gpg_strerror (err)); + return err; +} + + /* Perform the WRITEKEY operation. */ gpg_error_t app_writekey (app_t app, ctrl_t ctrl, @@ -759,7 +787,6 @@ app_writekey (app_t app, ctrl_t ctrl, if (opt.verbose) log_info ("operation writekey result: %s\n", gpg_strerror (err)); return err; - } diff --git a/scd/atr.c b/scd/atr.c index cb6b05a7d..67a86d09c 100644 --- a/scd/atr.c +++ b/scd/atr.c @@ -277,10 +277,121 @@ atr_dump (int slot, FILE *fp) } +/* Note: This code has not yet been tested! It shall return -1 on + error or the nu,ber of hiostroical bytes and store them at + HISTORICAL. */ +int +atr_get_historical (int slot, unsigned char historical[]) +{ + int result = -1; + unsigned char *atrbuffer = NULL; + unsigned char *atr; + size_t atrlen; + int have_ta, have_tb, have_tc, have_td; + int n_historical; + int idx; + unsigned char chksum; + atr = atrbuffer = apdu_get_atr (slot, &atrlen); + if (!atr || atrlen < 2) + goto leave; + atrlen--; + atr++; + chksum = *atr; + for (idx=1; idx < atrlen-1; idx++) + chksum ^= atr[idx]; + have_ta = !!(*atr & 0x10); + have_tb = !!(*atr & 0x20); + have_tc = !!(*atr & 0x40); + have_td = !!(*atr & 0x80); + n_historical = (*atr & 0x0f); + if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen) + goto leave; /* ATR shorter than indicated by format character. */ + atrlen--; + atr++; + if (have_ta + have_tb + have_tc >= atrlen) + goto leave; + atrlen -= have_ta + have_tb + have_tc; + atr += have_ta + have_tb + have_tc; + if (have_td) + { + have_ta = !!(*atr & 0x10); + have_tb = !!(*atr & 0x20); + have_tc = !!(*atr & 0x40); + have_td = !!(*atr & 0x80); + if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen) + goto leave; /* ATR shorter than indicated by format character. */ + atrlen--; + atr++; + } + else + have_ta = have_tb = have_tc = have_td = 0; + + if (have_ta + have_tb + have_tc >= atrlen) + goto leave; + atrlen -= have_ta + have_tb + have_tc; + atr += have_ta + have_tb + have_tc; + + if (have_td) + { + have_ta = !!(*atr & 0x10); + have_tb = !!(*atr & 0x20); + have_tc = !!(*atr & 0x40); + have_td = !!(*atr & 0x80); + if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen) + goto leave; /* ATR shorter than indicated by format character. */ + atrlen--; + atr++; + } + else + have_ta = have_tb = have_tc = have_td = 0; + + for (idx = 3; have_ta || have_tb || have_tc || have_td; idx++) + { + if (have_ta + have_tb + have_tc >= atrlen) + goto leave; + atrlen -= have_ta + have_tb + have_tc; + atr += have_ta + have_tb + have_tc; + + if (have_td) + { + have_ta = !!(*atr & 0x10); + have_tb = !!(*atr & 0x20); + have_tc = !!(*atr & 0x40); + have_td = !!(*atr & 0x80); + if (have_ta + have_tb + have_tc + have_td + n_historical >= atrlen) + goto leave; /* ATR shorter than indicated by format character. */ + atrlen--; + atr++; + } + else + have_ta = have_tb = have_tc = have_td = 0; + } + + if (n_historical >= atrlen) + goto leave; /* ATR shorter than required for historical bytes. */ + + if (n_historical) + { + for (idx=0; n_historical && atrlen; n_historical--, atrlen--, atr++) + historical[idx] = *atr; + } + + if (!atrlen || *atr != chksum) + goto leave; + + /* Don't care about garbage at the end of the ATR. */ + + result = n_historical; + + leave: + xfree (atrbuffer); + + return result; +} diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c index 79a42a423..899f7ae17 100644 --- a/scd/ccid-driver.c +++ b/scd/ccid-driver.c @@ -1957,7 +1957,7 @@ ccid_transceive_apdu_level (ccid_driver_t handle, size_t *nresp) { int rc; - unsigned char send_buffer[10+259], recv_buffer[10+259]; + unsigned char send_buffer[10+261], recv_buffer[10+261]; const unsigned char *apdu; size_t apdulen; unsigned char *msg; @@ -1971,7 +1971,9 @@ ccid_transceive_apdu_level (ccid_driver_t handle, apdulen = apdu_buflen; assert (apdulen); - if (apdulen > 254) + /* The maximum length for a short APDU T=1 block is 261, for an + extra APDU T=1 block is 65544. */ + if (apdulen > 261) return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ msg[0] = PC_to_RDR_XfrBlock; @@ -2117,6 +2119,7 @@ ccid_transceive (ccid_driver_t handle, assert (apdulen); /* Construct an I-Block. */ +#warning fixme: APDULEN may be larger if (apdulen > 254) return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */ diff --git a/scd/command.c b/scd/command.c index 418ad001e..696fa67ee 100644 --- a/scd/command.c +++ b/scd/command.c @@ -47,6 +47,9 @@ /* Maximum allowed size of key data as used in inquiries. */ #define MAXLEN_KEYDATA 4096 +/* Maximum allowed size of certificate data as used in inquiries. */ +#define MAXLEN_CERTDATA 16384 + #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t)) @@ -851,14 +854,8 @@ cmd_pksign (assuan_context_t ctx, char *line) hash_algo = GCRY_MD_SHA1; else return set_error (GPG_ERR_ASS_PARAMETER, "invalid hash algorithm"); - /* Skip over options. */ - while ( *line == '-' && line[1] == '-' ) - { - while (*line && !spacep (line)) - line++; - while (spacep (line)) - line++; - } + + line = skip_options (line); if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); @@ -1036,8 +1033,8 @@ cmd_getattr (assuan_context_t ctx, char *line) names and values are depend on the currently selected smartcard application. NAME and VALUE must be percent and '+' escaped. - However, the curent implementation assumes that Name is not escaped; - this works as long as noone uses arbitrary escaping. + However, the current implementation assumes that NAME is not + escaped; this works as long as noone uses arbitrary escaping. A PIN will be requested for most NAMEs. See the corresponding setattr function of the actually used application (app-*.c) for @@ -1083,12 +1080,74 @@ cmd_setattr (assuan_context_t ctx, char *orig_line) +/* WRITECERT + + This command is used to store a certifciate on a smartcard. The + allowed certids depend on the currently selected smartcard + application. The actual certifciate is requested using the inquiry + "CERTDATA" and needs to be provided in its raw (e.g. DER) form. + + In almost all cases a a PIN will be requested. See the related + writecert function of the actually used application (app-*.c) for + details. */ +static int +cmd_writecert (assuan_context_t ctx, char *line) +{ + ctrl_t ctrl = assuan_get_pointer (ctx); + int rc; + char *certid; + unsigned char *certdata; + size_t certdatalen; + + if ( IS_LOCKED (ctrl) ) + return gpg_error (GPG_ERR_LOCKED); + + line = skip_options (line); + + if (!*line) + return set_error (GPG_ERR_ASS_PARAMETER, "no certid given"); + certid = line; + while (*line && !spacep (line)) + line++; + *line = 0; + + if ((rc = open_card (ctrl, NULL))) + return rc; + + if (!ctrl->app_ctx) + return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION); + + certid = xtrystrdup (certid); + if (!certid) + return out_of_core (); + + /* Now get the actual keydata. */ + rc = assuan_inquire (ctx, "CERTDATA", + &certdata, &certdatalen, MAXLEN_CERTDATA); + if (rc) + { + xfree (certid); + return rc; + } + + /* Write the certificate to the card. */ + rc = app_writecert (ctrl->app_ctx, ctrl, certid, + pin_cb, ctx, certdata, certdatalen); + xfree (certid); + xfree (certdata); + + TEST_CARD_REMOVAL (ctrl, rc); + return rc; +} + + + /* WRITEKEY [--force] This command is used to store a secret key on a a smartcard. The allowed keyids depend on the currently selected smartcard application. The actual keydata is requested using the inquiry - "KETDATA" and need to be provided without any protection. With + "KEYDATA" and need to be provided without any protection. With --force set an existing key under this KEYID will get overwritten. The keydata is expected to be the usual canonical encoded S-expression. @@ -1109,14 +1168,8 @@ cmd_writekey (assuan_context_t ctx, char *line) if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - /* Skip over options. */ - while ( *line == '-' && line[1] == '-' ) - { - while (*line && !spacep (line)) - line++; - while (spacep (line)) - line++; - } + line = skip_options (line); + if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no keyid given"); keyid = line; @@ -1294,14 +1347,8 @@ cmd_passwd (assuan_context_t ctx, char *line) if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); - /* Skip over options. */ - while (*line == '-' && line[1] == '-') - { - while (*line && !spacep (line)) - line++; - while (spacep (line)) - line++; - } + line = skip_options (line); + if (!*line) return set_error (GPG_ERR_ASS_PARAMETER, "no CHV number given"); chvnostr = line; @@ -1601,14 +1648,7 @@ cmd_apdu (assuan_context_t ctx, char *line) with_atr = has_option (line, "--atr"); handle_more = has_option (line, "--more"); - /* Skip over options. */ - while ( *line == '-' && line[1] == '-' ) - { - while (*line && !spacep (line)) - line++; - while (spacep (line)) - line++; - } + line = skip_options (line); if ( IS_LOCKED (ctrl) ) return gpg_error (GPG_ERR_LOCKED); @@ -1687,6 +1727,7 @@ register_commands (assuan_context_t ctx) { "OUTPUT", NULL }, { "GETATTR", cmd_getattr }, { "SETATTR", cmd_setattr }, + { "WRITECERT", cmd_writecert }, { "WRITEKEY", cmd_writekey }, { "GENKEY", cmd_genkey }, { "RANDOM", cmd_random }, diff --git a/scd/iso7816.c b/scd/iso7816.c index b38ae4fd0..75a8f4a6a 100644 --- a/scd/iso7816.c +++ b/scd/iso7816.c @@ -66,7 +66,10 @@ map_sw (int sw) switch (sw) { case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break; + case SW_TERM_STATE: ec = GPG_ERR_CARD; break; case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break; + case SW_SM_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; break; + case SW_CC_NOT_SUP: ec = GPG_ERR_NOT_SUPPORTED; 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; @@ -130,7 +133,7 @@ iso7816_select_application (int slot, const char *aid, size_t aidlen, unsigned int flags) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, + sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, 4, (flags&1)? 0 :0x0c, aidlen, aid); return map_sw (sw); } @@ -156,7 +159,7 @@ iso7816_select_file (int slot, int tag, int is_dir, { p0 = (tag == 0x3F00)? 0: is_dir? 1:2; p1 = 0x0c; /* No FC return. */ - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, + sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, p0, p1, 2, (char*)tagbuf ); return map_sw (sw); } @@ -192,7 +195,7 @@ iso7816_select_path (int slot, const unsigned short *path, size_t pathlen, p0 = 0x08; p1 = 0x0c; /* No FC return. */ - sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, + sw = apdu_send_simple (slot, 0, 0x00, CMD_SELECT_FILE, p0, p1, buflen, (char*)buffer ); return map_sw (sw); } @@ -253,7 +256,7 @@ iso7816_verify_kp (int slot, int chvno, const char *chv, size_t chvlen, pininfo->maxlen, pininfo->padlen); else - sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); + sw = apdu_send_simple (slot, 0, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv); return map_sw (sw); } @@ -300,7 +303,7 @@ iso7816_change_reference_data_kp (int slot, int chvno, pininfo->maxlen, pininfo->padlen); else - sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA, + sw = apdu_send_simple (slot, 0, 0x00, CMD_CHANGE_REFERENCE_DATA, oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf); xfree (buf); return map_sw (sw); @@ -340,7 +343,7 @@ iso7816_reset_retry_counter_kp (int slot, int chvno, pininfo->maxlen, pininfo->padlen); else - sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER, + sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER, 2, chvno, newchvlen, newchv); return map_sw (sw); } @@ -386,14 +389,16 @@ iso7816_get_data (int slot, int tag, /* Perform a PUT DATA command on card in SLOT. Write DATA of length - DATALEN to TAG. */ + DATALEN to TAG. EXTENDED_MODE controls whether extended length + headers or command chaining is used instead of single length + bytes. */ gpg_error_t -iso7816_put_data (int slot, int tag, +iso7816_put_data (int slot, int extended_mode, int tag, const unsigned char *data, size_t datalen) { int sw; - sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA, + sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA, ((tag >> 8) & 0xff), (tag & 0xff), datalen, (const char*)data); return map_sw (sw); @@ -412,7 +417,7 @@ iso7816_manage_security_env (int slot, int p1, int p2, if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 ) return gpg_error (GPG_ERR_INV_VALUE); - sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2, + sw = apdu_send_simple (slot, 0, 0x00, CMD_MSE, p1, p2, data? datalen : -1, (const char*)data); return map_sw (sw); } diff --git a/scd/iso7816.h b/scd/iso7816.h index ebc121afc..19568184d 100644 --- a/scd/iso7816.h +++ b/scd/iso7816.h @@ -79,7 +79,7 @@ gpg_error_t iso7816_reset_retry_counter_kp (int slot, int chvno, iso7816_pininfo_t *pininfo); 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, +gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag, const unsigned char *data, size_t datalen); gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2, const unsigned char *data, diff --git a/sm/certdump.c b/sm/certdump.c index 9dbd24566..2dfbce105 100644 --- a/sm/certdump.c +++ b/sm/certdump.c @@ -982,7 +982,7 @@ gpgsm_format_keydesc (ksba_cert_t cert) /* We also escape the quote character to work around a bug in the mingw32 runtime which does not correcty handle command line quoting. We correctly double the quote mark when - calling a program (i.e. gpg-protec-tool), but the pre-main + calling a program (i.e. gpg-protect-tool), but the pre-main code does not notice the double quote as an escaped quote. */ if (*s < ' ' || *s == '+' || *s == '\"')