summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--agent/pksign.c2
-rw-r--r--doc/gpg.texi3
-rw-r--r--g10/ChangeLog11
-rw-r--r--g10/call-agent.c57
-rw-r--r--g10/call-agent.h4
-rw-r--r--g10/card-util.c172
-rw-r--r--scd/ChangeLog26
-rw-r--r--scd/apdu.c150
-rw-r--r--scd/apdu.h8
-rw-r--r--scd/app-common.h14
-rw-r--r--scd/app-openpgp.c182
-rw-r--r--scd/app.c33
-rw-r--r--scd/atr.c111
-rw-r--r--scd/ccid-driver.c7
-rw-r--r--scd/command.c111
-rw-r--r--scd/iso7816.c25
-rw-r--r--scd/iso7816.h2
-rw-r--r--sm/certdump.c2
18 files changed, 746 insertions, 174 deletions
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 <wk@g10code.com>
+
+ * 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 <dshaw@jabberwocky.com>
* 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;
@@ -445,6 +452,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 */
static int
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 <wk@g10code.com>
+
+ * 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 <wk@g10code.com>
+
+ * 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 <moritz@gnu.org>
* 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)
- {
- 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)
+ do
{
- 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 <hexified_certid>
+
+ 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] <keyid>
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 == '\"')