aboutsummaryrefslogtreecommitdiffstats
path: root/scd
diff options
context:
space:
mode:
Diffstat (limited to 'scd')
-rw-r--r--scd/ChangeLog31
-rw-r--r--scd/Makefile.am1
-rw-r--r--scd/app-common.h90
-rw-r--r--scd/app-openpgp.c390
-rw-r--r--scd/app.c208
-rw-r--r--scd/card.c3
-rw-r--r--scd/command.c290
-rw-r--r--scd/sc-investigate.c14
-rw-r--r--scd/scdaemon.c4
-rw-r--r--scd/scdaemon.h5
10 files changed, 970 insertions, 66 deletions
diff --git a/scd/ChangeLog b/scd/ChangeLog
index 6a8668b45..a6407b940 100644
--- a/scd/ChangeLog
+++ b/scd/ChangeLog
@@ -1,3 +1,34 @@
+2003-06-26 Werner Koch <[email protected]>
+
+ * app-openpgp.c (find_tlv): Fixed length header parsing.
+
+ * app.c (app_genkey): New.
+ * command.c (cmd_genkey): New.
+
+2003-06-25 Werner Koch <[email protected]>
+
+ * command.c (percent_plus_unescape): New.
+ (cmd_setattr): New.
+
+2003-06-24 Werner Koch <[email protected]>
+
+ * command.c (send_status_info): New.
+
+ * app-openpgp.c (app_select_openpgp): Replace SLOT arg by APP arg
+ and setup the function pointers in APP on success. Changed callers.
+ * app.c: New.
+ * app-common.h: New.
+ * scdaemon.h (APP): New type to handle applications.
+ (server_control_s): Add an APP context field.
+
+ * command.c (cmd_serialno): Handle applications.
+ (cmd_pksign): Ditto.
+ (cmd_pkdecrypt): Ditto.
+ (reset_notify): Ditto.
+ (cmd_learn): For now return error for application contexts.
+ (cmd_readcert): Ditto.
+ (cmd_readkey): Ditto.
+
2003-06-04 Werner Koch <[email protected]>
* card.c (map_sc_err): Renamed gpg_make_err to gpg_err_make.
diff --git a/scd/Makefile.am b/scd/Makefile.am
index 55d195f11..833a4cdf9 100644
--- a/scd/Makefile.am
+++ b/scd/Makefile.am
@@ -33,6 +33,7 @@ scdaemon_SOURCES = \
card-p15.c card-dinsig.c \
apdu.c apdu.h \
iso7816.c iso7816.h \
+ app.c app-common.h \
app-openpgp.c
scdaemon_LDADD = ../jnlib/libjnlib.a ../common/libcommon.a \
diff --git a/scd/app-common.h b/scd/app-common.h
new file mode 100644
index 000000000..16ee7d708
--- /dev/null
+++ b/scd/app-common.h
@@ -0,0 +1,90 @@
+/* app-common.h - Common declarations for all card applications
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#ifndef GNUPG_SCD_APP_COMMON_H
+#define GNUPG_SCD_APP_COMMON_H
+
+struct app_ctx_s {
+ int initialized; /* The application has been initialied and the
+ function pointers may be used. Note that for
+ unsupported operations the particular
+ function pointer is set to NULL */
+ int slot; /* Used reader. */
+ unsigned char *serialno; /* Serialnumber in raw form, allocated. */
+ size_t serialnolen; /* Length in octets of serialnumber. */
+ int did_chv1;
+ int did_chv2;
+ int did_chv3;
+ struct {
+ int (*learn_status) (APP app, CTRL ctrl);
+ int (*setattr) (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+ int (*sign) (APP app,
+ const char *keyidstr, int hashalgo,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen );
+ int (*decipher) (APP app, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen);
+ int (*genkey) (APP app, CTRL ctrl,
+ const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ } fnc;
+
+
+};
+
+/*-- app.c --*/
+APP select_application (void);
+int app_get_serial_and_stamp (APP app, char **serial, time_t *stamp);
+int app_write_learn_status (APP app, CTRL ctrl);
+int app_setattr (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+int app_sign (APP app, const char *keyidstr, int hashalgo,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen );
+int app_decipher (APP app, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen );
+int app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+
+/*-- app-openpgp.c --*/
+int app_select_openpgp (APP app, unsigned char **sn, size_t *snlen);
+
+
+#endif /*GNUPG_SCD_APP_COMMON_H*/
+
+
+
diff --git a/scd/app-openpgp.c b/scd/app-openpgp.c
index 82f004347..47bebed2c 100644
--- a/scd/app-openpgp.c
+++ b/scd/app-openpgp.c
@@ -26,8 +26,11 @@
#include <dlfcn.h>
#include "scdaemon.h"
+#include "app-common.h"
#include "iso7816.h"
+
+
static struct {
int tag;
int constructed;
@@ -80,6 +83,12 @@ find_tlv (const unsigned char *buffer, size_t length,
buffer = s;
if (n < 2)
return NULL; /* buffer definitely too short for tag and length. */
+ if (!*s || *s == 0xff)
+ { /* Skip optional filler between TLV objects. */
+ s++;
+ n--;
+ continue;
+ }
composite = !!(*s & 0x20);
if ((*s & 0x1f) == 0x1f)
{ /* more tag bytes to follow */
@@ -95,13 +104,26 @@ find_tlv (const unsigned char *buffer, size_t length,
this_tag = s[0];
len = s[1];
s += 2; n -= 2;
- if (len == 255)
- {
+ if (len < 0x80)
+ ;
+ else if (len == 0x81)
+ { /* One byte length follows. */
+ if (!n)
+ return NULL; /* we expected 1 more bytes with the length. */
+ len = s[0];
+ s++; n--;
+ }
+ else if (len == 0x82)
+ { /* Two byte length follows. */
if (n < 2)
return NULL; /* we expected 2 more bytes with the length. */
len = (s[0] << 8) | s[1];
s += 2; n -= 2;
}
+ else
+ return NULL; /* APDU limit is 65535, thus it does not make
+ sense to assume longer length fields. */
+
if (composite && nestlevel < 100)
{ /* Dive into this composite DO after checking for too deep
nesting. */
@@ -128,7 +150,64 @@ find_tlv (const unsigned char *buffer, size_t length,
}
+/* Get the DO identified by TAG from the card in SLOT and return a
+ buffer with its content in RESULT and NBYTES. The return value is
+ NULL if not found or a pointer which must be used to release the
+ buffer holding value. */
+static void *
+get_one_do (int slot, int tag, unsigned char **result, size_t *nbytes)
+{
+ int rc, i;
+ unsigned char *buffer;
+ size_t buflen;
+ unsigned char *value;
+ size_t valuelen;
+
+ *result = NULL;
+ *nbytes = 0;
+ for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
+ ;
+
+ value = NULL;
+ rc = -1;
+ if (data_objects[i].tag && data_objects[i].get_from)
+ {
+ rc = iso7816_get_data (slot, data_objects[i].get_from,
+ &buffer, &buflen);
+ if (!rc)
+ {
+ value = find_tlv (buffer, buflen, tag, &valuelen, 0);
+ if (!value)
+ ; /* not found */
+ else if (valuelen > buflen - (value - buffer))
+ {
+ log_error ("warning: constructed DO too short\n");
+ value = NULL;
+ xfree (buffer); buffer = NULL;
+ }
+ }
+ }
+
+ if (!value) /* Not in a constructed DO, try simple. */
+ {
+ rc = iso7816_get_data (slot, tag, &buffer, &buflen);
+ if (!rc)
+ {
+ value = buffer;
+ valuelen = buflen;
+ }
+ }
+
+ if (!rc)
+ {
+ *nbytes = valuelen;
+ *result = value;
+ return buffer;
+ }
+ return NULL;
+}
+#if 0 /* not used */
static void
dump_one_do (int slot, int tag)
{
@@ -191,6 +270,7 @@ dump_one_do (int slot, int tag)
xfree (buffer);
}
}
+#endif /*not used*/
static void
@@ -257,15 +337,15 @@ dump_all_do (int slot)
}
}
-
+/* Note, that FPR must be at least 20 bytes. */
static int
store_fpr (int slot, int keynumber, u32 timestamp,
const unsigned char *m, size_t mlen,
- const unsigned char *e, size_t elen)
+ const unsigned char *e, size_t elen,
+ unsigned char *fpr)
{
unsigned int n;
unsigned char *buffer, *p;
- unsigned char fpr[20];
int rc;
n = 6 + 2 + mlen + 2 + elen;
@@ -299,45 +379,189 @@ store_fpr (int slot, int keynumber, u32 timestamp,
return rc;
}
+
+static void
+send_fpr_if_not_null (CTRL ctrl, const char *keyword,
+ int number, const unsigned char *fpr)
+{
+ int i;
+ char buf[41];
+ char numbuf[25];
+
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ if (i==20)
+ return; /* All zero. */
+ for (i=0; i< 20; i++)
+ sprintf (buf+2*i, "%02X", fpr[i]);
+ if (number == -1)
+ *numbuf = 0; /* Don't print the key number */
+ else
+ sprintf (numbuf, "%d", number);
+ send_status_info (ctrl, keyword,
+ numbuf, (size_t)strlen(numbuf),
+ buf, (size_t)strlen (buf), NULL, 0);
+}
+
+static void
+send_key_data (CTRL ctrl, const char *name,
+ const unsigned char *a, size_t alen)
+{
+ char *p, *buf = xmalloc (alen*2+1);
+
+ for (p=buf; alen; a++, alen--, p += 2)
+ sprintf (p, "%02X", *a);
+
+ send_status_info (ctrl, "KEY-DATA",
+ name, (size_t)strlen(name),
+ buf, (size_t)strlen (buf),
+ NULL, 0);
+ xfree (buf);
+}
-/* Generate a new key on the card and store the fingerprint in the
- corresponding DO. A KEYNUMBER of 0 creates the digital signature
- key, 1 the encryption key and 2 the authentication key. If the key
- already exists an error is returned unless FORCE has been set to
- true. Note, that the function does not return the public key; this
- has to be done using openpgp_readkey(). */
-int
-openpgp_genkey (int slot, int keynumber, int force)
+
+static int
+do_learn_status (APP app, CTRL ctrl)
+{
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int i;
+
+ relptr = get_one_do (app->slot, 0x005B, &value, &valuelen);
+ if (relptr)
+ {
+ send_status_info (ctrl, "DISP-NAME", value, valuelen, NULL, 0);
+ xfree (relptr);
+ }
+ relptr = get_one_do (app->slot, 0x5FF0, &value, &valuelen);
+ if (relptr)
+ {
+ send_status_info (ctrl, "PUBKEY-URL", value, valuelen, NULL, 0);
+ xfree (relptr);
+ }
+
+ relptr = get_one_do (app->slot, 0x00C5, &value, &valuelen);
+ if (relptr && valuelen >= 60)
+ {
+ for (i=0; i < 3; i++)
+ send_fpr_if_not_null (ctrl, "KEY-FPR", i+1, value+i*20);
+ }
+ xfree (relptr);
+ relptr = get_one_do (app->slot, 0x00C6, &value, &valuelen);
+ if (relptr && valuelen >= 60)
+ {
+ for (i=0; i < 3; i++)
+ send_fpr_if_not_null (ctrl, "CA-FPR", i+1, value+i*20);
+ }
+ xfree (relptr);
+ return 0;
+}
+
+
+/* Handle the SETATTR operation. All arguments are already basically
+ checked. */
+static int
+do_setattr (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ gpg_error_t rc;
+
+ log_debug ("app_openpgp#setattr `%s' value of length %u\n",
+ name, (unsigned int)valuelen); /* fixme: name should be
+ sanitized. */
+
+ if (!app->did_chv3)
+ {
+ char *pinvalue;
+
+/* rc = pincb (pincb_arg, "Please enter the card's admin PIN (CHV3)", */
+/* &pinvalue); */
+ pinvalue = xstrdup ("12345678");
+ rc = 0;
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ if (rc)
+ {
+ log_error ("verify CHV3 failed\n");
+ rc = gpg_error (GPG_ERR_GENERAL);
+ return rc;
+ }
+ app->did_chv3 = 1;
+ }
+
+ log_debug ("setting `%s' to `%.*s'\n", name, (int)valuelen, value);
+ if (!strcmp (name, "DISP-NAME"))
+ {
+ rc = iso7816_put_data (app->slot, 0x005B, value, valuelen);
+ if (rc)
+ {
+ /* FIXME: If this fails we should *once* try again after
+ doing a verify command, so that in case of a problem with
+ tracking the verify operation we have a fallback. */
+ /* FIXME: change this when iso7816 returns correct error
+ codes. */
+ log_error ("failed to set `Name'\n");
+ rc = gpg_error (GPG_ERR_GENERAL);
+ }
+ }
+ else
+ rc = gpg_error (GPG_ERR_INV_NAME);
+
+ return rc;
+}
+
+
+/* Handle the GENKEY command. */
+static int
+do_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ 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;
time_t created_at;
-
- if (keynumber < 0 || keynumber > 2)
- return -1; /* invalid value */
+ int keyno = atoi (keynostr);
+ int force = (flags & 1);
- rc = iso7816_get_data (slot, 0x006E, &buffer, &buflen);
+ if (keyno < 1 || keyno > 3)
+ return gpg_error (GPG_ERR_INV_ID);
+ keyno--;
+
+ rc = iso7816_get_data (app->slot, 0x006E, &buffer, &buflen);
if (rc)
{
log_error ("error reading application data\n");
- return -1;
+ return gpg_error (GPG_ERR_GENERAL);
}
fpr = find_tlv (buffer, buflen, 0x00C5, &n, 0);
if (!fpr || n != 60)
{
+ rc = gpg_error (GPG_ERR_GENERAL);
log_error ("error reading fingerprint DO\n");
goto leave;
}
- fpr += 20*keynumber;
+ 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;
}
@@ -346,8 +570,7 @@ openpgp_genkey (int slot, int keynumber, int force)
else
log_info ("generating new key\n");
-
- rc = iso7816_verify (slot, 0x83, "12345678", 8);
+ rc = iso7816_verify (app->slot, 0x83, "12345678", 8);
if (rc)
{
log_error ("verify CHV3 failed: rc=%04X\n", rc);
@@ -355,13 +578,14 @@ openpgp_genkey (int slot, int keynumber, int force)
}
xfree (buffer); buffer = NULL;
- rc = iso7816_generate_keypair (slot,
- keynumber == 0? "\xB6" :
- keynumber == 1? "\xB8" : "\xA4",
+ rc = iso7816_generate_keypair (app->slot,
+ keyno == 0? "\xB6" :
+ keyno == 1? "\xB8" : "\xA4",
2,
&buffer, &buflen);
if (rc)
{
+ rc = gpg_error (GPG_ERR_CARD);
log_error ("generating key failed\n");
goto leave;
}
@@ -372,7 +596,6 @@ openpgp_genkey (int slot, int keynumber, int force)
goto leave;
}
-
m = find_tlv (keydata, keydatalen, 0x0081, &mlen, 0);
if (!m)
{
@@ -380,6 +603,8 @@ openpgp_genkey (int slot, int keynumber, int force)
goto leave;
}
log_printhex ("RSA n:", m, mlen);
+ send_key_data (ctrl, "n", m, mlen);
+
e = find_tlv (keydata, keydatalen, 0x0082, &elen, 0);
if (!e)
{
@@ -387,8 +612,18 @@ openpgp_genkey (int slot, int keynumber, int force)
goto leave;
}
log_printhex ("RSA e:", e, elen);
+ send_key_data (ctrl, "e", e, elen);
+
created_at = gnupg_get_time ();
- rc = store_fpr (slot, keynumber, (u32)created_at, m, mlen, e, elen);
+ sprintf (numbuf, "%lu", (unsigned long)created_at);
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+ rc = store_fpr (app->slot, keyno, (u32)created_at,
+ m, mlen, e, elen, fprbuf);
+ if (rc)
+ goto leave;
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
leave:
@@ -397,12 +632,75 @@ openpgp_genkey (int slot, int keynumber, int force)
}
+/* Comopute a digital signature on INDATA which is expected to be the
+ raw message digest. */
+static int
+do_sign (APP app, const char *keyidstr, int hashalgo,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen )
+{
+ static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ int rc;
+ unsigned char data[35];
+
+ /* We ignore KEYIDSTR, because the OpenPGP application has only one
+ signing key and no way to specify a different one. */
+
+ if (indatalen != 20)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (hashalgo == GCRY_MD_SHA1)
+ memcpy (data, sha1_prefix, 15);
+ else if (hashalgo == GCRY_MD_RMD160)
+ memcpy (data, rmd160_prefix, 15);
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data+15, indata, indatalen);
+
+
+ if (!app->did_chv1)
+ {
+ char *pinvalue;
+
+/* rc = pincb (pincb_arg, "signature PIN", &pinvalue); */
+ pinvalue = xstrdup ("123456");
+ rc = 0;
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ if (rc)
+ {
+ log_error ("verify CHV1 failed\n");
+ rc = gpg_error (GPG_ERR_GENERAL);
+ return rc;
+ }
+ app->did_chv1 = 1;
+ }
+
+ rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+
+ return rc;
+}
+
+
/* Select the OpenPGP application on the card in SLOT. This function
- must be used to before any other OpenPGP application functions. */
+ must be used before any other OpenPGP application functions. */
int
-app_select_openpgp (int slot)
+app_select_openpgp (APP app, unsigned char **sn, size_t *snlen)
{
static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
+ int slot = app->slot;
int rc;
unsigned char *buffer;
size_t buflen;
@@ -416,27 +714,31 @@ app_select_openpgp (int slot)
if (rc)
goto leave;
if (opt.verbose)
- log_info ("got AID: ");
- log_printhex ("", buffer, buflen);
- xfree (buffer);
+ {
+ log_info ("got AID: ");
+ log_printhex ("", buffer, buflen);
+ }
+
+ if (sn)
+ {
+ *sn = buffer;
+ *snlen = buflen;
+ }
+ else
+ xfree (buffer);
dump_all_do (slot);
-/* rc = iso7816_verify (slot, 0x83, "12345678", 8); */
-/* if (rc) */
-/* log_error ("verify CHV3 failed: rc=%04X\n", rc); */
-
-
-/* rc = iso7816_put_data (slot, 0x005B, "Joe Hacker", 10); */
-/* if (rc) */
-/* log_error ("failed to set `Name': rc=%04X\n", rc); */
-/* else */
-/* dump_one_do (slot, 0x005B); */
-
- /* fixme: associate the internal state with the slot */
- }
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.setattr = do_setattr;
+ app->fnc.genkey = do_genkey;
+ app->fnc.sign = do_sign;
+ }
leave:
return rc;
}
+
+
+
diff --git a/scd/app.c b/scd/app.c
new file mode 100644
index 000000000..3688b1c8d
--- /dev/null
+++ b/scd/app.c
@@ -0,0 +1,208 @@
+/* app.c - Application selection.
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <dlfcn.h>
+
+#include "scdaemon.h"
+#include "app-common.h"
+#include "apdu.h"
+
+/* The select the best fitting application and return a context.
+ Returns NULL if no application was found or no card is present. */
+APP
+select_application (void)
+{
+ int reader_port = 32768; /* First USB reader. */
+ int slot;
+ int rc;
+ APP app;
+
+ slot = apdu_open_reader (reader_port);
+ if (slot == -1)
+ {
+ log_error ("card reader not available\n");
+ return NULL;
+ }
+
+ app = xtrycalloc (1, sizeof *app);
+ if (!app)
+ {
+ rc = out_of_core ();
+ log_info ("error allocating context: %s\n", gpg_strerror (rc));
+ /*apdu_close_reader (slot);*/
+ return NULL;
+ }
+
+ app->slot = slot;
+ rc = app_select_openpgp (app, &app->serialno, &app->serialnolen);
+ if (rc)
+ {
+/* apdu_close_reader (slot); */
+ log_info ("selecting openpgp failed: %s\n", gpg_strerror (rc));
+ xfree (app);
+ return NULL;
+ }
+
+ app->initialized = 1;
+ return app;
+}
+
+
+
+/* Retrieve the serial number and the time of the last update of the
+ card. The serial number is returned as a malloced string (hex
+ encoded) in SERIAL and the time of update is returned in STAMP. If
+ no update time is available the returned value is 0. Caller must
+ free SERIAL unless the function returns an error. */
+int
+app_get_serial_and_stamp (APP app, char **serial, time_t *stamp)
+{
+ unsigned char *buf, *p;
+ int i;
+
+ if (!app || !serial || !stamp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ *serial = NULL;
+ *stamp = 0; /* not available */
+
+ buf = xtrymalloc (app->serialnolen * 2 + 1);
+ if (!buf)
+ return gpg_error_from_errno (errno);
+ for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
+ sprintf (p, "%02X", app->serialno[i]);
+ *p = 0;
+ *serial = buf;
+ return 0;
+}
+
+
+/* Write out the application specifig status lines for the LEARN
+ command. */
+int
+app_write_learn_status (APP app, CTRL ctrl)
+{
+ if (!app)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.learn_status)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ return app->fnc.learn_status (app, ctrl);
+}
+
+
+/* Perform a SETATTR operation. */
+int
+app_setattr (APP app, const char *name,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ if (!app || !name || !*name || !value)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.setattr)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ return app->fnc.setattr (app, name, pincb, pincb_arg, value, valuelen);
+}
+
+/* Create the signature and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+app_sign (APP app, const char *keyidstr, int hashalgo,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen )
+{
+ int rc;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.sign)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.sign (app, keyidstr, hashalgo,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("operation sign result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Decrypt the data in INDATA and return the allocated result in OUTDATA.
+ If a PIN is required the PINCB will be used to ask for the PIN; it
+ should return the PIN in an allocated buffer and put it into PIN. */
+int
+app_decipher (APP app, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ void **outdata, size_t *outdatalen )
+{
+ int rc;
+
+ if (!app || !indata || !indatalen || !outdata || !outdatalen || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.decipher)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.decipher (app, keyidstr,
+ pincb, pincb_arg,
+ indata, indatalen,
+ outdata, outdatalen);
+ if (opt.verbose)
+ log_info ("operation decipher result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Perform a SETATTR operation. */
+int
+app_genkey (APP app, CTRL ctrl, const char *keynostr, unsigned int flags,
+ int (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+
+ if (!app || !keynostr || !*keynostr || !pincb)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!app->initialized)
+ return gpg_error (GPG_ERR_CARD_NOT_INITIALIZED);
+ if (!app->fnc.genkey)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+ rc = app->fnc.genkey (app, ctrl, keynostr, flags, pincb, pincb_arg);
+ if (opt.verbose)
+ log_info ("operation genkey result: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
diff --git a/scd/card.c b/scd/card.c
index 574867a58..e46bb9d21 100644
--- a/scd/card.c
+++ b/scd/card.c
@@ -59,7 +59,7 @@ map_sc_err (int rc)
int
card_help_get_keygrip (KsbaCert cert, unsigned char *array)
{
- GCRY_SEXP s_pkey;
+ gcry_sexp_t s_pkey;
int rc;
KsbaSexp p;
size_t n;
@@ -558,3 +558,4 @@ card_decipher (CARD card, const char *keyidstr,
log_info ("card operation decipher result: %s\n", gpg_strerror (rc));
return rc;
}
+
diff --git a/scd/command.c b/scd/command.c
index 8267c13ab..9f164f28e 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -30,6 +30,7 @@
#include <assuan.h>
#include "scdaemon.h"
+#include "app-common.h"
/* maximum length aloowed as a PIN; used for INQUIRE NEEDPIN */
#define MAXLEN_PIN 100
@@ -69,6 +70,12 @@ reset_notify (ASSUAN_CONTEXT ctx)
xfree (ctrl->in_data.value);
ctrl->in_data.value = NULL;
}
+ if (ctrl->app_ctx)
+ {
+ /* FIXME: close the application. */
+ xfree (ctrl->app_ctx);
+ ctrl->app_ctx = NULL;
+ }
}
@@ -85,8 +92,14 @@ option_handler (ASSUAN_CONTEXT ctx, const char *key, const char *value)
static AssuanError
open_card (CTRL ctrl)
{
- if (!ctrl->card_ctx)
- {
+ if (ctrl->app_ctx)
+ return 0; /* Already initialized for one specific application. */
+ if (ctrl->card_ctx)
+ return 0; /* Already initialized using a card context. */
+
+ ctrl->app_ctx = select_application ();
+ if (!ctrl->app_ctx)
+ { /* No application found - fall back to old mode. */
int rc = card_open (&ctrl->card_ctx);
if (rc)
return map_to_assuan_status (rc);
@@ -95,6 +108,41 @@ open_card (CTRL ctrl)
}
+/* Do the percent and plus/space unescaping in place and return tghe
+ length of the valid buffer. */
+static size_t
+percent_plus_unescape (unsigned char *string)
+{
+ unsigned char *p = string;
+ size_t n = 0;
+
+ while (*string)
+ {
+ if (*string == '%' && string[1] && string[2])
+ {
+ string++;
+ *p++ = xtoi_2 (string);
+ n++;
+ string+= 2;
+ }
+ else if (*string == '+')
+ {
+ *p++ = ' ';
+ n++;
+ string++;
+ }
+ else
+ {
+ *p++ = *string++;
+ n++;
+ }
+ }
+
+ return n;
+}
+
+
+
/* SERIALNO
Return the serial number of the card using a status reponse. This
@@ -106,7 +154,7 @@ open_card (CTRL ctrl)
Background: We want to keep the client clear of handling card
changes between operations; i.e. the client can assume that all
- operations are doneon the same card unless he call this function.
+ operations are done on the same card unless he call this function.
*/
static int
cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
@@ -120,7 +168,10 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
if ((rc = open_card (ctrl)))
return rc;
- rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
if (rc)
return map_to_assuan_status (rc);
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
@@ -149,6 +200,16 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
error message. The response of this command is a list of status
lines formatted as this:
+ S APPTYPE <apptype>
+
+ This returns the type of the application, currently the strings:
+
+ P15 = PKCS-15 structure used
+ DINSIG = DIN SIG
+ OPENPGP = OpenPGP card
+
+ are implemented. These strings are aliases for the AID
+
S KEYPAIRINFO <hexstring_with_keygrip> <hexstring_with_id>
If there is no certificate yet stored on the card a single "X" is
@@ -157,13 +218,34 @@ cmd_serialno (ASSUAN_CONTEXT ctx, char *line)
S CERTINFO <certtype> <hexstring_with_id>
- Where CERTINFO is a number indicating the type of certificate:
+ Where CERTTYPE is a number indicating the type of certificate:
0 := Unknown
100 := Regular X.509 cert
101 := Trusted X.509 cert
102 := Useful X.509 cert
+ For certain cards, more information will be returned:
+
+ S KEY-FPR <no> <hexstring>
+ For OpenPGP cards this returns the stored fingerprints of the
+ keys. This can be used check whether a key is available on the
+ card. NO may be 1, 2 or 3.
+
+ S CA-FPR <no> <hexstring>
+
+ Similar to above, these are the fingerprints of keys assumed to be
+ ultimately trusted.
+
+ S DISP-NAME <name_of_card_holder>
+
+ The name of the card holder as stored on the card; percent
+ aescaping takes place, spaces are encoded as '+'
+
+ S PUBKEY-URL <url>
+
+ The URL to be used for locating the entire public key.
+
*/
static int
cmd_learn (ASSUAN_CONTEXT ctx, char *line)
@@ -183,8 +265,11 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
char *serial_and_stamp;
char *serial;
time_t stamp;
-
- rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
+
+ if (ctrl->app_ctx)
+ rc = app_get_serial_and_stamp (ctrl->app_ctx, &serial, &stamp);
+ else
+ rc = card_get_serial_and_stamp (ctrl->card_ctx, &serial, &stamp);
if (rc)
return map_to_assuan_status (rc);
rc = asprintf (&serial_and_stamp, "%s %lu", serial, (unsigned long)stamp);
@@ -221,6 +306,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
}
/* Return information about the certificates. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
for (idx=0; !rc; idx++)
{
char *certid;
@@ -248,6 +335,8 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
/* Return information about the keys. */
+ if (ctrl->app_ctx)
+ rc = -1; /* This information is not yet available for applications. */
for (idx=0; !rc; idx++)
{
unsigned char keygrip[20];
@@ -294,6 +383,9 @@ cmd_learn (ASSUAN_CONTEXT ctx, char *line)
if (rc == -1)
rc = 0;
+ if (!rc && ctrl->app_ctx)
+ rc = app_write_learn_status (ctrl->app_ctx, ctrl);
+
return map_to_assuan_status (rc);
}
@@ -314,6 +406,9 @@ cmd_readcert (ASSUAN_CONTEXT ctx, char *line)
if ((rc = open_card (ctrl)))
return rc;
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
{
@@ -348,6 +443,9 @@ cmd_readkey (ASSUAN_CONTEXT ctx, char *line)
if ((rc = open_card (ctrl)))
return rc;
+ if (ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
rc = card_read_cert (ctrl->card_ctx, line, &cert, &ncert);
if (rc)
{
@@ -480,11 +578,19 @@ cmd_pksign (ASSUAN_CONTEXT ctx, char *line)
keyidstr = strdup (line);
if (!keyidstr)
return ASSUAN_Out_Of_Core;
- rc = card_sign (ctrl->card_ctx,
- keyidstr, GCRY_MD_SHA1,
- pin_cb, ctx,
- ctrl->in_data.value, ctrl->in_data.valuelen,
- &outdata, &outdatalen);
+
+ if (ctrl->app_ctx)
+ rc = app_sign (ctrl->app_ctx,
+ keyidstr, GCRY_MD_SHA1,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ rc = card_sign (ctrl->card_ctx,
+ keyidstr, GCRY_MD_SHA1,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
free (keyidstr);
if (rc)
{
@@ -519,11 +625,18 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
keyidstr = strdup (line);
if (!keyidstr)
return ASSUAN_Out_Of_Core;
- rc = card_decipher (ctrl->card_ctx,
- keyidstr,
- pin_cb, ctx,
- ctrl->in_data.value, ctrl->in_data.valuelen,
- &outdata, &outdatalen);
+ if (ctrl->app_ctx)
+ rc = app_decipher (ctrl->app_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
+ else
+ rc = card_decipher (ctrl->card_ctx,
+ keyidstr,
+ pin_cb, ctx,
+ ctrl->in_data.value, ctrl->in_data.valuelen,
+ &outdata, &outdatalen);
free (keyidstr);
if (rc)
{
@@ -541,6 +654,99 @@ cmd_pkdecrypt (ASSUAN_CONTEXT ctx, char *line)
}
+/* SETATTR <name> <value>
+
+ This command is used to store data on a a smartcard. The allowed
+ 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.
+
+ A PIN will be requested for most NAMEs. See the corresponding
+ setattr function of the actually used application (app-*.c) for
+ details. */
+static int
+cmd_setattr (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyword;
+ int keywordlen;
+ size_t nbytes;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ keyword = line;
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ if (*line)
+ *line++ = 0;
+ while (spacep (line))
+ line++;
+ nbytes = percent_plus_unescape (line);
+
+ rc = app_setattr (ctrl->app_ctx, keyword, pin_cb, ctx, line, nbytes);
+
+ return map_to_assuan_status (rc);
+}
+
+/* GENKEY [--force] <no>
+
+ Generate a key on-card identified by NO, which is application
+ specific. Return values are application specific. For OpenPGP
+ cards 2 status lines are returned:
+
+ S KEY-FPR <hexstring>
+ S KEY-CREATED-AT <seconds_since_epoch>
+ S KEY-DATA [p|n] <hexdata>
+
+
+ --force is required to overwriet 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.
+
+ The public part of the key can also later be retrieved using the
+ READKEY command.
+
+ */
+static int
+cmd_genkey (ASSUAN_CONTEXT ctx, char *line)
+{
+ CTRL ctrl = assuan_get_pointer (ctx);
+ int rc;
+ char *keyno;
+ int force = has_option (line, "--force");
+
+ /* Skip over options. */
+ while ( *line == '-' && line[1] == '-' )
+ {
+ while (!spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ }
+ if (!*line)
+ return set_error (Parameter_Error, "no key number given");
+ keyno = line;
+ while (!spacep (line))
+ line++;
+ *line = 0;
+
+ if ((rc = open_card (ctrl)))
+ return rc;
+
+ if (!ctrl->app_ctx)
+ return gpg_error (GPG_ERR_UNSUPPORTED_OPERATION);
+
+ rc = app_genkey (ctrl->app_ctx, ctrl, keyno, force? 1:0, pin_cb, ctx);
+
+ return map_to_assuan_status (rc);
+}
+
+
+
/* Tell the assuan library about our commands */
@@ -560,6 +766,8 @@ register_commands (ASSUAN_CONTEXT ctx)
{ "PKDECRYPT", cmd_pkdecrypt },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
+ { "SETATTR", cmd_setattr },
+ { "GENKEY", cmd_genkey },
{ NULL }
};
int i, rc;
@@ -646,3 +854,51 @@ scd_command_handler (int listen_fd)
assuan_deinit_server (ctx);
}
+
+
+/* Send a line with status information via assuan and escape all given
+ buffers. The variable elements are pairs of (char *, size_t),
+ terminated with a (NULL, 0). */
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ const unsigned char *value;
+ size_t valuelen;
+ char buf[950], *p;
+ size_t n;
+ ASSUAN_CONTEXT ctx = ctrl->server_local->assuan_ctx;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+ {
+ valuelen = va_arg (arg_ptr, size_t);
+ if (!valuelen)
+ continue; /* empty buffer */
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+ {
+ if (*value < ' ' || *value == '+')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ }
+ *p = 0;
+ assuan_write_status (ctx, keyword, buf);
+
+ va_end (arg_ptr);
+}
+
diff --git a/scd/sc-investigate.c b/scd/sc-investigate.c
index e6b4c4afb..1100bdf43 100644
--- a/scd/sc-investigate.c
+++ b/scd/sc-investigate.c
@@ -25,11 +25,12 @@
#include <string.h>
#define JNLIB_NEED_LOG_LOGV
-#include <gcrypt.h>
#include "scdaemon.h"
+#include <gcrypt.h>
#include "apdu.h" /* for open_reader */
#include "atr.h"
+#include "app-common.h"
#define _(a) (a)
@@ -104,6 +105,9 @@ main (int argc, char **argv )
ARGPARSE_ARGS pargs;
int slot, rc;
int reader_port = 32768; /* First USB reader. */
+ struct app_ctx_s appbuf;
+
+ memset (&appbuf, 0, sizeof appbuf);
set_strusage (my_strusage);
gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN);
@@ -147,7 +151,8 @@ main (int argc, char **argv )
if (rc)
log_error ("can't dump ATR: %s\n", gpg_strerror (rc));
- rc = app_select_openpgp (slot);
+ appbuf.slot = slot;
+ rc = app_select_openpgp (&appbuf, NULL, NULL);
if (rc)
log_error ("selecting openpgp failed: %s\n", gpg_strerror (rc));
else
@@ -159,3 +164,8 @@ main (int argc, char **argv )
+void
+send_status_info (CTRL ctrl, const char *keyword, ...)
+{
+ /* DUMMY */
+}
diff --git a/scd/scdaemon.c b/scd/scdaemon.c
index 5ab50a5b4..8e0ef37c9 100644
--- a/scd/scdaemon.c
+++ b/scd/scdaemon.c
@@ -34,12 +34,12 @@
#include <unistd.h>
#include <signal.h>
+#define JNLIB_NEED_LOG_LOGV
+#include "scdaemon.h"
#include <ksba.h>
#include <gcrypt.h>
-#define JNLIB_NEED_LOG_LOGV
#include <assuan.h> /* malloc hooks */
-#include "scdaemon.h"
#include "i18n.h"
#include "sysutils.h"
diff --git a/scd/scdaemon.h b/scd/scdaemon.h
index 59d40e519..394a13a1a 100644
--- a/scd/scdaemon.h
+++ b/scd/scdaemon.h
@@ -76,18 +76,22 @@ struct {
struct server_local_s;
struct card_ctx_s;
+struct app_ctx_s;
struct server_control_s {
struct server_local_s *server_local;
struct card_ctx_s *card_ctx;
+ struct app_ctx_s *app_ctx;
struct {
unsigned char *value;
int valuelen;
} in_data; /* helper to store the value we are going to sign */
};
+
typedef struct server_control_s *CTRL;
typedef struct card_ctx_s *CARD;
+typedef struct app_ctx_s *APP;
/*-- scdaemon.c --*/
void scd_exit (int rc);
@@ -95,6 +99,7 @@ void scd_init_default_ctrl (CTRL ctrl);
/*-- command.c --*/
void scd_command_handler (int);
+void send_status_info (CTRL ctrl, const char *keyword, ...);
/*-- card.c --*/
int card_open (CARD *rcard);