aboutsummaryrefslogtreecommitdiffstats
path: root/scd/card-p15.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--scd/card-p15.c502
1 files changed, 502 insertions, 0 deletions
diff --git a/scd/card-p15.c b/scd/card-p15.c
new file mode 100644
index 000000000..3cf4ba519
--- /dev/null
+++ b/scd/card-p15.c
@@ -0,0 +1,502 @@
+/* card-p15.c - PKCS-15 based card access
+ * Copyright (C) 2002 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 <time.h>
+
+#ifdef HAVE_OPENSC
+#include <opensc/pkcs15.h>
+#include <ksba.h>
+
+#include "scdaemon.h"
+#include "card-common.h"
+
+
+struct p15private_s {
+ int n_prkey_rsa_objs;
+ struct sc_pkcs15_object *prkey_rsa_objs[32];
+ int n_cert_objs;
+ struct sc_pkcs15_object *cert_objs[32];
+};
+
+
+/* Allocate private data. */
+static int
+init_private_data (CARD card)
+{
+ struct p15private_s *priv;
+ int rc;
+
+ if (card->p15priv)
+ return 0; /* already done. */
+
+ priv = xtrycalloc (1, sizeof *priv);
+ if (!priv)
+ return out_of_core ();
+
+ /* OpenSC (0.7.0) is a bit strange in that the get_objects functions
+ tries to be a bit too clever and implicitly does an enumeration
+ which eventually leads to the fact that every call to this
+ fucntion returns one more macthing object. The old code in
+ p15_enum_keypairs assume that it would alwyas return the same
+ numer of objects and used this to figure out what the last object
+ enumerated is. We now do an enum_objects just once and keep it
+ in the private data. */
+ rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA,
+ priv->prkey_rsa_objs,
+ DIM (priv->prkey_rsa_objs));
+ if (rc < 0)
+ {
+ log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
+ xfree (priv);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ priv->n_prkey_rsa_objs = rc;
+
+ /* Read all certificate objects. */
+ rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_CERT_X509,
+ priv->cert_objs,
+ DIM (priv->cert_objs));
+ if (rc < 0)
+ {
+ log_error ("private keys enumeration failed: %s\n", sc_strerror (rc));
+ xfree (priv);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ priv->n_cert_objs = rc;
+
+ card->p15priv = priv;
+ return 0;
+}
+
+
+/* Release private data used in this module. */
+void
+p15_release_private_data (CARD card)
+{
+ if (!card->p15priv)
+ return;
+ xfree (card->p15priv);
+ card->p15priv = NULL;
+}
+
+
+
+/* See card.c for interface description */
+static int
+p15_enum_keypairs (CARD card, int idx,
+ unsigned char *keygrip, char **keyid)
+{
+ int rc;
+ KsbaError krc;
+ struct p15private_s *priv;
+ struct sc_pkcs15_object *tmpobj;
+ int nobjs;
+ struct sc_pkcs15_prkey_info *pinfo;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ KsbaCert cert;
+
+ rc = init_private_data (card);
+ if (rc)
+ return rc;
+ priv = card->p15priv;
+ nobjs = priv->n_prkey_rsa_objs;
+ rc = 0;
+ if (idx >= nobjs)
+ return -1;
+ pinfo = priv->prkey_rsa_objs[idx]->data;
+
+ /* now we need to read the certificate so that we can calculate the
+ keygrip */
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &pinfo->id, &tmpobj);
+ if (rc)
+ {
+ log_info ("certificate for private key %d not found: %s\n",
+ idx, sc_strerror (rc));
+ /* note, that we return the ID anyway */
+ rc = gpg_error (GPG_ERR_MISSING_CERTIFICATE);
+ goto return_keyid;
+ }
+ certinfo = tmpobj->data;
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate for private key %d: %s\n",
+ idx, sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ cert = ksba_cert_new ();
+ if (!cert)
+ {
+ gpg_error_t tmperr = out_of_core ();
+ sc_pkcs15_free_certificate (certder);
+ return tmperr;
+ }
+ krc = ksba_cert_init_from_mem (cert, certder->data, certder->data_len);
+ sc_pkcs15_free_certificate (certder);
+ if (krc)
+ {
+ log_error ("failed to parse the certificate for private key %d: %s\n",
+ idx, ksba_strerror (krc));
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (card_help_get_keygrip (cert, keygrip))
+ {
+ log_error ("failed to calculate the keygrip of private key %d\n", idx);
+ ksba_cert_release (cert);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ ksba_cert_release (cert);
+
+ rc = 0;
+ return_keyid:
+ if (keyid)
+ {
+ char *p;
+ int i;
+
+ *keyid = p = xtrymalloc (9+pinfo->id.len*2+1);
+ if (!*keyid)
+ return out_of_core ();
+ p = stpcpy (p, "P15-5015.");
+ for (i=0; i < pinfo->id.len; i++, p += 2)
+ sprintf (p, "%02X", pinfo->id.value[i]);
+ *p = 0;
+ }
+
+ return rc;
+}
+
+/* See card.c for interface description */
+static int
+p15_enum_certs (CARD card, int idx, char **certid, int *type)
+{
+ int rc;
+ struct p15private_s *priv;
+ struct sc_pkcs15_object *obj;
+ struct sc_pkcs15_cert_info *cinfo;
+ int nobjs;
+
+ rc = init_private_data (card);
+ if (rc)
+ return rc;
+ priv = card->p15priv;
+ nobjs = priv->n_cert_objs;
+ rc = 0;
+ if (idx >= nobjs)
+ return -1;
+ obj = priv->cert_objs[idx];
+ cinfo = obj->data;
+
+ if (certid)
+ {
+ char *p;
+ int i;
+
+ *certid = p = xtrymalloc (9+cinfo->id.len*2+1);
+ if (!*certid)
+ return out_of_core ();
+ p = stpcpy (p, "P15-5015.");
+ for (i=0; i < cinfo->id.len; i++, p += 2)
+ sprintf (p, "%02X", cinfo->id.value[i]);
+ *p = 0;
+ }
+ if (type)
+ {
+ if (!obj->df)
+ *type = 0; /* unknown */
+ else if (obj->df->type == SC_PKCS15_CDF)
+ *type = 100;
+ else if (obj->df->type == SC_PKCS15_CDF_TRUSTED)
+ *type = 101;
+ else if (obj->df->type == SC_PKCS15_CDF_USEFUL)
+ *type = 102;
+ else
+ *type = 0; /* error -> unknown */
+ }
+
+ return rc;
+}
+
+
+
+static int
+idstr_to_id (const char *idstr, struct sc_pkcs15_id *id)
+{
+ const char *s;
+ int n;
+
+ /* For now we only support the standard DF */
+ if (strncmp (idstr, "P15-5015.", 9) )
+ return gpg_error (GPG_ERR_INV_ID);
+ for (s=idstr+9, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n&1))
+ return gpg_error (GPG_ERR_INV_ID); /*invalid or odd number of digits*/
+ n /= 2;
+ if (!n || n > SC_PKCS15_MAX_ID_SIZE)
+ return gpg_error (GPG_ERR_INV_ID); /* empty or too large */
+ for (s=idstr+9, n=0; *s; s += 2, n++)
+ id->value[n] = xtoi_2 (s);
+ id->len = n;
+ return 0;
+}
+
+
+/* See card.c for interface description */
+static int
+p15_read_cert (CARD card, const char *certidstr,
+ unsigned char **cert, size_t *ncert)
+{
+ struct sc_pkcs15_object *tmpobj;
+ struct sc_pkcs15_id certid;
+ struct sc_pkcs15_cert_info *certinfo;
+ struct sc_pkcs15_cert *certder;
+ int rc;
+
+ if (!card || !certidstr || !cert || !ncert)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (!card->p15card)
+ return gpg_error (GPG_ERR_NO_PKCS15_APP);
+
+ rc = idstr_to_id (certidstr, &certid);
+ if (rc)
+ return rc;
+
+ rc = sc_pkcs15_find_cert_by_id (card->p15card, &certid, &tmpobj);
+ if (rc)
+ {
+ log_info ("certificate '%s' not found: %s\n",
+ certidstr, sc_strerror (rc));
+ return -1;
+ }
+ certinfo = tmpobj->data;
+ rc = sc_pkcs15_read_certificate (card->p15card, certinfo, &certder);
+ if (rc)
+ {
+ log_info ("failed to read certificate '%s': %s\n",
+ certidstr, sc_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+ }
+
+ *cert = xtrymalloc (certder->data_len);
+ if (!*cert)
+ {
+ gpg_error_t tmperr = out_of_core ();
+ sc_pkcs15_free_certificate (certder);
+ return tmperr;
+ }
+ memcpy (*cert, certder->data, certder->data_len);
+ *ncert = certder->data_len;
+ sc_pkcs15_free_certificate (certder);
+ return 0;
+}
+
+
+
+
+
+static int
+p15_prepare_key (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg, struct sc_pkcs15_object **r_keyobj)
+{
+ struct sc_pkcs15_id keyid;
+ struct sc_pkcs15_pin_info *pin;
+ struct sc_pkcs15_object *keyobj, *pinobj;
+ char *pinvalue;
+ int rc;
+
+ rc = idstr_to_id (keyidstr, &keyid);
+ if (rc)
+ return rc;
+
+ rc = sc_pkcs15_find_prkey_by_id (card->p15card, &keyid, &keyobj);
+ if (rc < 0)
+ {
+ log_error ("private key not found: %s\n", sc_strerror(rc));
+ return gpg_error (GPG_ERR_NO_SECRET_KEY);
+ }
+
+ rc = sc_pkcs15_find_pin_by_auth_id (card->p15card,
+ &keyobj->auth_id, &pinobj);
+ if (rc)
+ {
+ log_error ("failed to find PIN by auth ID: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_BAD_PIN_METHOD);
+ }
+ pin = pinobj->data;
+
+ /* Fixme: pack this into a verification loop */
+ /* Fixme: we might want to pass pin->min_length and
+ pin->stored_length */
+ rc = pincb (pincb_arg, pinobj->label, &pinvalue);
+ if (rc)
+ {
+ log_info ("PIN callback returned error: %s\n", gnupg_strerror (rc));
+ return rc;
+ }
+
+ rc = sc_pkcs15_verify_pin (card->p15card, pin,
+ pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ if (rc)
+ {
+ log_info ("PIN verification failed: %s\n", sc_strerror (rc));
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ /* fixme: check wheter we need to release KEYOBJ in case of an error */
+ *r_keyobj = keyobj;
+ return 0;
+}
+
+
+/* See card.c for interface description */
+static int
+p15_sign (CARD card, const char *keyidstr, int hashalgo,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ unsigned int cryptflags;
+ struct sc_pkcs15_object *keyobj;
+ int rc;
+ unsigned char *outbuf = NULL;
+ size_t outbuflen;
+
+ if (hashalgo != GCRY_MD_SHA1)
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+
+ rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
+ if (rc)
+ return rc;
+
+ cryptflags = SC_ALGORITHM_RSA_PAD_PKCS1;
+
+ outbuflen = 1024;
+ outbuf = xtrymalloc (outbuflen);
+ if (!outbuf)
+ return out_of_core ();
+
+ rc = sc_pkcs15_compute_signature (card->p15card, keyobj,
+ cryptflags,
+ indata, indatalen,
+ outbuf, outbuflen );
+ if (rc < 0)
+ {
+ log_error ("failed to create signature: %s\n", sc_strerror (rc));
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ *outdatalen = rc;
+ *outdata = outbuf;
+ outbuf = NULL;
+ rc = 0;
+ }
+
+ xfree (outbuf);
+ return rc;
+}
+
+
+/* See card.c for description */
+static int
+p15_decipher (CARD card, const char *keyidstr,
+ int (pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ struct sc_pkcs15_object *keyobj;
+ int rc;
+ unsigned char *outbuf = NULL;
+ size_t outbuflen;
+
+ rc = p15_prepare_key (card, keyidstr, pincb, pincb_arg, &keyobj);
+ if (rc)
+ return rc;
+
+ if (card && card->scard && card->scard->driver
+ && !strcasecmp (card->scard->driver->short_name, "tcos"))
+ {
+ /* very ugly hack to force the use of a local key. We need this
+ until we have fixed the initialization code for TCOS cards */
+ struct sc_pkcs15_prkey_info *prkey = keyobj->data;
+ if ( !(prkey->key_reference & 0x80))
+ {
+ prkey->key_reference |= 0x80;
+ log_debug ("using TCOS hack to force the use of local keys\n");
+ }
+ if (*keyidstr && keyidstr[strlen(keyidstr)-1] == '6')
+ {
+ prkey->key_reference |= 1;
+ log_debug ("warning: using even more TCOS hacks\n");
+ }
+ }
+
+ outbuflen = indatalen < 256? 256 : indatalen;
+ outbuf = xtrymalloc (outbuflen);
+ if (!outbuf)
+ return out_of_core ();
+
+ rc = sc_pkcs15_decipher (card->p15card, keyobj,
+ 0,
+ indata, indatalen,
+ outbuf, outbuflen);
+ if (rc < 0)
+ {
+ log_error ("failed to decipher the data: %s\n", sc_strerror (rc));
+ rc = gpg_error (GPG_ERR_CARD);
+ }
+ else
+ {
+ *outdatalen = rc;
+ *outdata = outbuf;
+ outbuf = NULL;
+ rc = 0;
+ }
+
+ xfree (outbuf);
+ return rc;
+}
+
+
+
+/* Bind our operations to the card */
+void
+card_p15_bind (CARD card)
+{
+ card->fnc.enum_keypairs = p15_enum_keypairs;
+ card->fnc.enum_certs = p15_enum_certs;
+ card->fnc.read_cert = p15_read_cert;
+ card->fnc.sign = p15_sign;
+ card->fnc.decipher = p15_decipher;
+}
+#endif /*HAVE_OPENSC*/