aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
authorWerner Koch <[email protected]>2005-05-20 20:39:36 +0000
committerWerner Koch <[email protected]>2005-05-20 20:39:36 +0000
commit41862f5f13bef8113cf040ecaba34a35c370eeb3 (patch)
tree8deaa1ca3c63351b2ef028947aa445b422081e9f /scd
parentChanged the scdaemon to handle concurrent sessions. Adjusted (diff)
downloadgnupg-41862f5f13bef8113cf040ecaba34a35c370eeb3.tar.gz
gnupg-41862f5f13bef8113cf040ecaba34a35c370eeb3.zip
* protect-tool.c: New option --canonical.
(show_file): Implement it. * keyformat.txt: Define the created-at attribute for keys. * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level. (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good. (ccid_transceive): Arghhh. The seqno is another bit in the R-block than in the I block, this was wrong at one place. * scdaemon.c: New options --debug-ccid-driver and --debug-disable-ticker. * app-openpgp.c (do_genkey, do_writekey): Factored code to check for existing key out into .. (does_key_exist): .. New function. * gpg-connect-agent.c (add_definq, show_definq, clear_definq) (handle_inquire): New. (read_and_print_response): Handle INQUIRE command. (main): Implement control commands.
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog27
-rw-r--r--scd/app-common.h28
-rw-r--r--scd/app-openpgp.c355
-rw-r--r--scd/app.c29
-rw-r--r--scd/ccid-driver.c104
-rw-r--r--scd/command.c81
-rw-r--r--scd/scdaemon.c33
-rw-r--r--scd/tlv.c73
-rw-r--r--scd/tlv.h17
9 files changed, 650 insertions, 97 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 19bba2bf4..c64fbec7e 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,30 @@
+2005-05-20 Werner Koch <[email protected]>
+
+ * ccid-driver.c: Replaced macro DEBUG_T1 by a new debug level.
+ (parse_ccid_descriptor): Mark SCR335 firmware version 5.18 good.
+ (ccid_transceive): Arghhh. The seqno is another bit in the
+ R-block than in the I block, this was wrong at one place.
+
+ * scdaemon.c: New options --debug-ccid-driver and
+ --debug-disable-ticker.
+
+ * app-openpgp.c (do_genkey, do_writekey): Factored code to check
+ for existing key out into ..
+ (does_key_exist): .. New function.
+
+2005-05-19 Werner Koch <[email protected]>
+
+ * tlv.c (parse_sexp): New.
+
+ * command.c (cmd_writekey): New.
+ * app.c (app_writekey): New.
+ * app-common.c (app_t): Add function ptr WRITEKEY.
+ * app-openpgp.c (do_writekey): New.
+
+ * app-openpgp.c (do_readkey) [GNUPG_MAJOR_VERSION==1]: Return error.
+ * app-common.h (app_t) [GNUPG_MAJOR_VERSION==1]: Add a field to
+ store the Assuan context.
+
2005-05-17 Werner Koch <[email protected]>
* scdaemon.c: Removed non-pth code paths.
diff --git a/scd/app-common.h b/scd/app-common.h
index 517286c49..c2c302395 100644
--- a/scd/app-common.h
+++ b/scd/app-common.h
@@ -23,10 +23,15 @@
#ifndef GNUPG_SCD_APP_COMMON_H
#define GNUPG_SCD_APP_COMMON_H
-#if GNUPG_MAJOR_VERSION != 1
-#include <ksba.h>
+#if GNUPG_MAJOR_VERSION == 1
+# ifdef ENABLE_AGENT_SUPPORT
+# include "assuan.h"
+# endif
+#else
+# include <ksba.h>
#endif
+
struct app_local_s; /* Defined by all app-*.c. */
struct app_ctx_s {
@@ -35,6 +40,15 @@ struct app_ctx_s {
unsupported operations the particular
function pointer is set to NULL */
int slot; /* Used reader. */
+
+ /* If this is used by GnuPG 1.4 we need to know the assuan context
+ in case we need to divert the operation to an already running
+ agent. This if ASSUAN_CTX is not NULL we take this as indication
+ that all operations are diverted to gpg-agent. */
+#if GNUPG_MAJOR_VERSION == 1 && defined(ENABLE_AGENT_SUPPORT)
+ assuan_context_t assuan_ctx;
+#endif /*GNUPG_MAJOR_VERSION == 1*/
+
unsigned char *serialno; /* Serialnumber in raw form, allocated. */
size_t serialnolen; /* Length in octets of serialnumber. */
const char *apptype;
@@ -72,6 +86,11 @@ struct app_ctx_s {
void *pincb_arg,
const void *indata, size_t indatalen,
unsigned char **outdata, size_t *outdatalen);
+ gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
+ const char *certid, unsigned int flags,
+ gpg_error_t (*pincb)(void*,const char *,char **),
+ void *pincb_arg,
+ const unsigned char *pk, size_t pklen);
gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
@@ -134,6 +153,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_writekey (app_t app, ctrl_t ctrl,
+ const char *keyidstr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen);
gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
const char *keynostr, unsigned int flags,
gpg_error_t (*pincb)(void*, const char *, char **),
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index b8060df03..16ebd34c8 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -565,7 +565,7 @@ store_fpr (int slot, int keynumber, u32 timestamp,
n = 6 + 2 + mlen + 2 + elen;
p = buffer = xtrymalloc (3 + n);
if (!buffer)
- return gpg_error (gpg_err_code_from_errno (errno));
+ return gpg_error_from_errno (errno);
*p++ = 0x99; /* ctb */
*p++ = n >> 8; /* 2 byte length header */
@@ -1207,6 +1207,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
static gpg_error_t
do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
{
+#if GNUPG_MAJOR_VERSION > 1
gpg_error_t err;
int keyno;
unsigned char *buf;
@@ -1230,6 +1231,9 @@ do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
*pk = buf;
*pklen = app->app_local->pk[keyno-1].keylen;;
return 0;
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
}
@@ -1523,6 +1527,318 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
}
+/* Check whether a key already exists. KEYIDX is the index of the key
+ (0..2). If FORCE is TRUE a diagnositivc will be printed but no
+ error returned if the key already exists. */
+static gpg_error_t
+does_key_exist (app_t app, int keyidx, int force)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int i;
+
+ assert (keyidx >=0 && keyidx <= 2);
+
+ if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n < 60)
+ {
+ log_error (_("error reading fingerprint DO\n"));
+ xfree (buffer);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += 20*keyidx;
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ xfree (buffer);
+ if (i!=20 && !force)
+ {
+ log_error (_("key already exists\n"));
+ return gpg_error (GPG_ERR_EEXIST);
+ }
+ else if (i!=20)
+ log_info (_("existing key will be replaced\n"));
+ else
+ log_info (_("generating new key\n"));
+ return 0;
+}
+
+
+
+/* Handle the WRITEKEY command for OpenPGP. This function expects a
+ canonical encoded S-expression with the secret key in KEYDATA and
+ its length (for assertions) in KEYDATALEN. KEYID needs to be the
+ usual keyid which for OpenPGP is the string "OPENPGP.n" with
+ n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall
+ get overwritten. PINCB and PINCB_ARG are the usual arguments for
+ the pinentry callback. */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+ int force = (flags & 1);
+ int keyno;
+ const unsigned char *buf, *tok;
+ size_t buflen, toklen;
+ int depth, last_depth1, last_depth2;
+ const unsigned char *rsa_n = NULL;
+ const unsigned char *rsa_e = NULL;
+ const unsigned char *rsa_p = NULL;
+ const unsigned char *rsa_q = NULL;
+ size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned int nbits;
+ unsigned char *template = NULL;
+ unsigned char *tp;
+ size_t template_len;
+ unsigned char fprbuf[20];
+ u32 created_at = 0;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 0;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 2;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = does_key_exist (app, keyno, force);
+ if (err)
+ return err;
+
+
+ /*
+ Parse the S-expression
+ */
+ buf = keydata;
+ buflen = keydatalen;
+ depth = 0;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+ {
+ if (!tok)
+ ;
+ else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+ log_info ("protected-private-key passed to writekey\n");
+ else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+ log_info ("shadowed-private-key passed to writekey\n");
+ err = gpg_error (GPG_ERR_BAD_KEY);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+ {
+ err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+ goto leave;
+ }
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 1)
+ {
+ const unsigned char **mpi;
+ size_t *mpi_len;
+
+ switch (*tok)
+ {
+ case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
+ case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
+ case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
+ case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+ if (mpi && *mpi)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && mpi)
+ {
+ /* Strip off leading zero bytes and save. */
+ for (;toklen && !*tok; toklen--, tok++)
+ ;
+ *mpi = tok;
+ *mpi_len = toklen;
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+ /* Parse other attributes. */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+ {
+ if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+ goto leave;
+ if (tok)
+ {
+ for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+ tok++, toklen--)
+ created_at = created_at*10 + (*tok - '0');
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+ if (!created_at)
+ {
+ log_error (_("creation timestamp missing\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
+ if (nbits != 1024)
+ {
+ log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
+ if (nbits < 2 || nbits > 32)
+ {
+ log_error (_("RSA public exponent missing or largerr than %d bits\n"),
+ 32);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+
+ /* Build the private key template as described in section 4.3.3.6 of
+ the OpenPGP card specs:
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
+ */
+ assert (rsa_e_len <= 4);
+ template_len = (1 + 1 + 4
+ + 1 + 1 + rsa_p_len
+ + 1 + 1 + rsa_q_len);
+ template = tp = xtrymalloc_secure (template_len);
+ if (!template)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < 4)
+ {
+ /* Right justify E. */
+ memmove (tp+4-rsa_e_len, tp, 4-rsa_e_len);
+ memset (tp, 0, 4-rsa_e_len);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = rsa_p_len;
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ *tp++ = 0xC2;
+ *tp++ = rsa_q_len;
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ assert (tp - template == template_len);
+
+
+ /* Obviously we need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ err = iso7816_put_data (app->slot,
+ (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
+ template, template_len);
+ if (err)
+ {
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = store_fpr (app->slot, keyno, created_at,
+ rsa_n, rsa_n_len, rsa_e, rsa_e_len,
+ fprbuf, app->card_version);
+ if (err)
+ goto leave;
+
+
+ leave:
+ xfree (template);
+ return err;
+}
+
/* Handle the GENKEY command. */
static gpg_error_t
@@ -1531,13 +1847,11 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
void *pincb_arg)
{
int rc;
- int i;
char numbuf[30];
unsigned char fprbuf[20];
- const unsigned char *fpr;
const unsigned char *keydata, *m, *e;
- unsigned char *buffer;
- size_t buflen, keydatalen, n, mlen, elen;
+ unsigned char *buffer = NULL;
+ size_t buflen, keydatalen, mlen, elen;
time_t created_at;
int keyno = atoi (keynostr);
int force = (flags & 1);
@@ -1558,41 +1872,15 @@ do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
app->app_local->pk[keyno].read_done = 0;
/* Check whether a key already exists. */
- rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
+ rc = does_key_exist (app, keyno, force);
if (rc)
- {
- log_error (_("error reading application data\n"));
- return gpg_error (GPG_ERR_GENERAL);
- }
- fpr = find_tlv (buffer, buflen, 0x00C5, &n);
- if (!fpr || n != 60)
- {
- rc = gpg_error (GPG_ERR_GENERAL);
- log_error (_("error reading fingerprint DO\n"));
- goto leave;
- }
- fpr += 20*keyno;
- for (i=0; i < 20 && !fpr[i]; i++)
- ;
- if (i!=20 && !force)
- {
- rc = gpg_error (GPG_ERR_EEXIST);
- log_error (_("key already exists\n"));
- goto leave;
- }
- else if (i!=20)
- log_info (_("existing key will be replaced\n"));
- else
- log_info (_("generating new key\n"));
+ return rc;
-
/* Prepare for key generation by verifying the ADmin PIN. */
rc = verify_chv3 (app, pincb, pincb_arg);
if (rc)
goto leave;
- xfree (buffer); buffer = NULL;
-
#if 1
log_info (_("please wait while key is being generated ...\n"));
start_at = time (NULL);
@@ -2216,6 +2504,7 @@ app_select_openpgp (app_t app)
app->fnc.readkey = do_readkey;
app->fnc.getattr = do_getattr;
app->fnc.setattr = do_setattr;
+ app->fnc.writekey = do_writekey;
app->fnc.genkey = do_genkey;
app->fnc.sign = do_sign;
app->fnc.auth = do_auth;
diff --git a/scd/app.c b/scd/app.c
index 0a1960267..f2c427f5b 100644
--- a/scd/app.c
+++ b/scd/app.c
@@ -546,6 +546,35 @@ app_decipher (app_t app, const char *keyidstr,
}
+/* Perform the WRITEKEY operation. */
+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 **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+
+ if (!app || !keyidstr || !*keyidstr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.writekey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ err = lock_reader (app);
+ if (err)
+ return err;
+ err = app->fnc.writekey (app, ctrl, keyidstr, flags,
+ pincb, pincb_arg, keydata, keydatalen);
+ unlock_reader (app);
+ if (opt.verbose)
+ log_info ("operation writekey result: %s\n", gpg_strerror (err));
+ return err;
+
+}
+
+
/* Perform a SETATTR operation. */
gpg_error_t
app_genkey (app_t app, CTRL ctrl, const char *keynostr, unsigned int flags,
diff --git a/scd/ccid-driver.c b/scd/ccid-driver.c
index b817452b1..387108559 100644
--- a/scd/ccid-driver.c
+++ b/scd/ccid-driver.c
@@ -108,9 +108,6 @@
# include "scdaemon.h"
#endif
-/* Define to print information pertaining the T=1 protocol. */
-#undef DEBUG_T1
-
# define DEBUGOUT(t) do { if (debug_level) \
log_debug (DRVNAME t); } while (0)
@@ -120,6 +117,8 @@
log_debug (DRVNAME t,(a),(b)); } while (0)
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
log_debug (DRVNAME t,(a),(b),(c));} while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b),(c),(d));} while (0)
# define DEBUGOUT_CONT(t) do { if (debug_level) \
log_printf (t); } while (0)
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
@@ -141,6 +140,8 @@
fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0)
# define DEBUGOUT_CONT(t) do { if (debug_level) \
fprintf (stderr, t); } while (0)
# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
@@ -216,7 +217,11 @@ struct ccid_driver_s
static int initialized_usb; /* Tracks whether USB has been initialized. */
-static int debug_level; /* Flag to control the debug output. */
+static int debug_level; /* Flag to control the debug output.
+ 0 = No debugging
+ 1 = USB I/O info
+ 2 = T=1 protocol tracing
+ */
static unsigned int compute_edc (const unsigned char *data, size_t datalen,
@@ -457,7 +462,7 @@ parse_ccid_descriptor (ccid_driver_t handle,
&& handle->max_ifsd > 48
&& ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
- ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0519)
+ ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0518)
||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
))
{
@@ -827,7 +832,8 @@ scan_or_find_devices (int readerno, const char *readerid,
/* Set the level of debugging to to usea dn return the old level. -1
just returns the old level. A level of 0 disables debugging, 1
- enables debugging, other values are not yet defined. */
+ enables debugging, 2 enables additional tracing of the T=1
+ protocol, other values are not yet defined. */
int
ccid_set_debug_level (int level)
{
@@ -1437,12 +1443,13 @@ ccid_get_atr (ccid_driver_t handle,
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
-#ifdef DEBUG_T1
- fprintf (stderr, "T1: put %c-block seq=%d\n",
- ((msg[11] & 0xc0) == 0x80)? 'R' :
- (msg[11] & 0x80)? 'S' : 'I',
- ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
-#endif
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
rc = bulk_out (handle, msg, msglen);
if (rc)
@@ -1460,14 +1467,15 @@ ccid_get_atr (ccid_driver_t handle,
if (tpdulen < 4)
return CCID_DRIVER_ERR_ABORTED;
-#ifdef DEBUG_T1
- fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
- ((msg[11] & 0xc0) == 0x80)? 'R' :
- (msg[11] & 0x80)? 'S' : 'I',
- ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
- ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
- );
-#endif
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
{
DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
@@ -1706,12 +1714,13 @@ ccid_transceive (ccid_driver_t handle,
DEBUGOUT_CONT_1 (" %02X", msg[i]);
DEBUGOUT_LF ();
-#ifdef DEBUG_T1
- fprintf (stderr, "T1: put %c-block seq=%d\n",
- ((msg[11] & 0xc0) == 0x80)? 'R' :
- (msg[11] & 0x80)? 'S' : 'I',
- ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)));
-#endif
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
rc = bulk_out (handle, msg, msglen);
if (rc)
@@ -1731,14 +1740,14 @@ ccid_transceive (ccid_driver_t handle,
usb_clear_halt (handle->idev, handle->ep_bulk_in);
return CCID_DRIVER_ERR_ABORTED;
}
-#ifdef DEBUG_T1
- fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
- ((msg[11] & 0xc0) == 0x80)? 'R' :
- (msg[11] & 0x80)? 'S' : 'I',
- ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
- ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
- );
-#endif
+
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
@@ -1814,8 +1823,8 @@ ccid_transceive (ccid_driver_t handle,
msg = send_buffer;
tpdulen = last_tpdulen;
}
- else if (sending && !!(tpdu[1] & 0x40) == handle->t1_ns)
- { /* Reponse does not match our sequence number. */
+ else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
+ { /* Response does not match our sequence number. */
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
return CCID_DRIVER_ERR_CARD_IO_ERROR;
}
@@ -1835,7 +1844,7 @@ ccid_transceive (ccid_driver_t handle,
else
{ /* This is a S-block. */
retries = 0;
- DEBUGOUT_2 ("T1 S-block %s received cmd=%d\n",
+ DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
@@ -1853,7 +1862,7 @@ ccid_transceive (ccid_driver_t handle,
if (use_crc)
tpdu[tpdulen++] = (edc >> 8);
tpdu[tpdulen++] = edc;
- DEBUGOUT_1 ("T1 waittime extension of bwi=%d\n", bwi);
+ DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi);
}
else
return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2008,14 +2017,13 @@ ccid_transceive_secure (ccid_driver_t handle,
usb_clear_halt (handle->idev, handle->ep_bulk_in);
return CCID_DRIVER_ERR_ABORTED;
}
-#ifdef DEBUG_T1
- fprintf (stderr, "T1: got %c-block seq=%d err=%d\n",
- ((msg[11] & 0xc0) == 0x80)? 'R' :
- (msg[11] & 0x80)? 'S' : 'I',
- ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
- ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0
- );
-#endif
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
if (!(tpdu[1] & 0x80))
{ /* This is an I-block. */
@@ -2062,7 +2070,7 @@ ccid_transceive_secure (ccid_driver_t handle,
DEBUGOUT ("No retries supported for Secure operation\n");
return CCID_DRIVER_ERR_CARD_IO_ERROR;
}
- else if (!!(tpdu[1] & 0x40) == handle->t1_ns)
+ else if (!!(tpdu[1] & 0x10) == handle->t1_ns)
{ /* Reponse does not match our sequence number. */
DEBUGOUT ("R-block with wrong seqno received on more bit\n");
return CCID_DRIVER_ERR_CARD_IO_ERROR;
@@ -2075,7 +2083,7 @@ ccid_transceive_secure (ccid_driver_t handle,
}
else
{ /* This is a S-block. */
- DEBUGOUT_2 ("T1 S-block %s received cmd=%d for Secure operation\n",
+ DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n",
(tpdu[1] & 0x20)? "response": "request",
(tpdu[1] & 0x1f));
return CCID_DRIVER_ERR_CARD_IO_ERROR;
diff --git a/scd/command.c b/scd/command.c
index 5ea3e01db..c68d0e925 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -40,6 +40,9 @@
/* Maximum length allowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100
+/* Maximum allowed size of key data as used in inquiries. */
+#define MAXLEN_KEYDATA 4096
+
#define set_error(e,t) assuan_set_error (ctx, ASSUAN_ ## e, (t))
@@ -523,7 +526,7 @@ cmd_readcert (assuan_context_t ctx, char *line)
}
-/* READKEY <hexified_certid>
+/* READKEY <keyid>
Return the public key for the given cert or key ID as an standard
S-Expression.
@@ -913,6 +916,79 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
return map_to_assuan_status (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
+ --force set an existing key under this KEYID will get overwritten.
+ The keydata is expected to be the usual canonical encoded
+ S-expression.
+
+ A PIN will be requested for most NAMEs. See the corresponding
+ writekey function of the actually used application (app-*.c) for
+ details. */
+static int
+cmd_writekey (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyid;
+ int force = has_option (line, "--force");
+ unsigned char *keydata;
+ size_t keydatalen;
+
+ 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++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no keyid given");
+ keyid = 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);
+
+ keyid = xtrystrdup (keyid);
+ if (!keyid)
+ return ASSUAN_Out_Of_Core;
+
+ /* Now get the actual keydata. */
+ rc = assuan_inquire (ctx, "KEYDATA", &keydata, &keydatalen, MAXLEN_KEYDATA);
+ if (rc)
+ {
+ xfree (keyid);
+ return rc;
+ }
+
+ /* Write the key to the card. */
+ rc = app_writekey (ctrl->app_ctx, ctrl, keyid, force? 1:0,
+ pin_cb, ctx, keydata, keydatalen);
+ xfree (keyid);
+ xfree (keydata);
+
+ TEST_CARD_REMOVAL (ctrl, rc);
+ return map_to_assuan_status (rc);
+}
+
+
+
/* GENKEY [--force] <no>
Generate a key on-card identified by NO, which is application
@@ -924,7 +1000,7 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
S KEY-DATA [p|n] <hexdata>
- --force is required to overwriet an already existing key. The
+ --force is required to overwrite an already existing key. The
KEY-CREATED-AT is required for further processing because it is
part of the hashed key material for the fingerprint.
@@ -1222,6 +1298,7 @@ register_commands (assuan_context_t ctx)
{ "OUTPUT", NULL },
{ "GETATTR", cmd_getattr },
{ "SETATTR", cmd_setattr },
+ { "WRITEKEY", cmd_writekey },
{ "GENKEY", cmd_genkey },
{ "RANDOM", cmd_random },
{ "PASSWD", cmd_passwd },
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 9a8b31ac5..1110d9d76 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -50,6 +50,7 @@
#ifdef HAVE_W32_SYSTEM
#include "../jnlib/w32-afunix.h"
#endif
+#include "ccid-driver.h"
enum cmd_and_opt_values
@@ -66,7 +67,7 @@ enum cmd_and_opt_values
oDebugAll,
oDebugLevel,
oDebugWait,
- oDebugSC,
+ oDebugCCIDDriver,
oNoGreeting,
oNoOptions,
oHomedir,
@@ -85,8 +86,8 @@ enum cmd_and_opt_values
oAllowAdmin,
oDenyAdmin,
oDisableApplication,
-
-aTest };
+ oDebugDisableTicker
+};
@@ -97,6 +98,8 @@ static ARGPARSE_OPTS opts[] = {
{ 301, NULL, 0, N_("@Options:\n ") },
{ oServer, "server", 0, N_("run in server mode (foreground)") },
+ { oMultiServer, "multi-server", 0,
+ N_("run in multi server mode (foreground)") },
{ oDaemon, "daemon", 0, N_("run in daemon mode (background)") },
{ oVerbose, "verbose", 0, N_("verbose") },
{ oQuiet, "quiet", 0, N_("be somewhat more quiet") },
@@ -107,10 +110,10 @@ static ARGPARSE_OPTS opts[] = {
{ oDebugAll, "debug-all" ,0, "@"},
{ oDebugLevel, "debug-level" ,2, "@"},
{ oDebugWait,"debug-wait",1, "@"},
+ { oDebugCCIDDriver, "debug-ccid-driver", 0, "@"},
+ { oDebugDisableTicker, "debug-disable-ticker", 0, "@"},
{ oNoDetach, "no-detach" ,0, N_("do not detach from the console")},
{ oLogFile, "log-file" ,2, N_("use a log file for the server")},
- { oMultiServer, "multi-server", 0,
- N_("allow additional connections in server mode")},
{ oReaderPort, "reader-port", 2, N_("|N|connect to reader at port N")},
{ octapiDriver, "ctapi-driver", 2, N_("|NAME|use NAME as ct-API driver")},
{ opcscDriver, "pcsc-driver", 2, N_("|NAME|use NAME as PC/SC driver")},
@@ -125,10 +128,6 @@ static ARGPARSE_OPTS opts[] = {
{ oDenyAdmin, "deny-admin", 0, "@" },
{ oDisableApplication, "disable-application", 2, "@"},
- /* Dummy options to be removed at some point. */
- { oDebugSC, "debug-sc", 1, "@" },
- { oDisableOpenSC, "disable-opensc", 0, "@" },
-
{0}
};
@@ -150,6 +149,12 @@ static int maybe_setuid = 1;
/* Name of the communication socket */
static char *socket_name;
+
+/* Debug flag to disable the ticker. The ticker is in fact not
+ disabled but it won't perform any ticker specific actions. */
+static int ticker_disabled;
+
+
static char *create_socket_name (int use_standard_socket,
char *standard_name, char *template);
@@ -443,7 +448,10 @@ main (int argc, char **argv )
case oDebugAll: opt.debug = ~0; break;
case oDebugLevel: debug_level = pargs.r.ret_str; break;
case oDebugWait: debug_wait = pargs.r.ret_int; break;
- case oDebugSC: break;
+ case oDebugCCIDDriver:
+ ccid_set_debug_level (ccid_set_debug_level (-1)+1);
+ break;
+ case oDebugDisableTicker: ticker_disabled = 1; break;
case oOptions:
/* config files may not be nested (silently ignore them) */
@@ -463,7 +471,7 @@ main (int argc, char **argv )
case oCsh: csh_style = 1; break;
case oSh: csh_style = 0; break;
case oServer: pipe_server = 1; break;
- case oMultiServer: multi_server = 1; break;
+ case oMultiServer: pipe_server = 1; multi_server = 1; break;
case oDaemon: is_daemon = 1; break;
case oReaderPort: opt.reader_port = pargs.r.ret_str; break;
@@ -839,7 +847,8 @@ handle_signal (int signo)
static void
handle_tick (void)
{
- scd_update_reader_status_file ();
+ if (!ticker_disabled)
+ scd_update_reader_status_file ();
}
diff --git a/scd/tlv.c b/scd/tlv.c
index 3a81ea6d9..b5dcd4021 100644
--- a/scd/tlv.c
+++ b/scd/tlv.c
@@ -221,3 +221,76 @@ parse_ber_header (unsigned char const **buffer, size_t *size,
*size = length;
return 0;
}
+
+
+/* FIXME: The following function should not go into this file but for
+ now it is easier to keep it here. */
+
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return:
+
+ depth = 0;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth)
+ process_token (tok, toklen);
+ if (err)
+ handle_error ();
+ */
+gpg_error_t
+parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen)
+{
+ const unsigned char *s;
+ size_t n, vlen;
+
+ s = *buf;
+ n = *buflen;
+ *tok = NULL;
+ *toklen = 0;
+ if (!n)
+ return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
+ if (*s == '(')
+ {
+ s++; n--;
+ (*depth)++;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ if (*s == ')')
+ {
+ if (!*depth)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *toklen = 1;
+ s++; n--;
+ (*depth)--;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
+ vlen = vlen*10 + (*s - '0');
+ if (!n || *s != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++; n--;
+ if (vlen > n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *tok = s;
+ *toklen = vlen;
+ s += vlen;
+ n -= vlen;
+ *buf = s;
+ *buflen = n;
+ return 0;
+}
+
diff --git a/scd/tlv.h b/scd/tlv.h
index 628580431..f587dd9df 100644
--- a/scd/tlv.h
+++ b/scd/tlv.h
@@ -88,4 +88,21 @@ gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return. */
+gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen);
+
+
+
#endif /* SCD_TLV_H */