aboutsummaryrefslogtreecommitdiffstats
path: root/scd/app-openpgp.c
diff options
context:
space:
mode:
Diffstat (limited to 'scd/app-openpgp.c')
-rw-r--r--scd/app-openpgp.c390
1 files changed, 346 insertions, 44 deletions
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;
}
+
+
+