/* 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 #include #include #include #include #include #include #include #include "scdaemon.h" #include "card-common.h" /* 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 sc_pkcs15_object *objs[32], *tmpobj; int nobjs; struct sc_pkcs15_prkey_info *pinfo; struct sc_pkcs15_cert_info *certinfo; struct sc_pkcs15_cert *certder; KsbaCert cert; rc = sc_pkcs15_get_objects (card->p15card, SC_PKCS15_TYPE_PRKEY_RSA, objs, DIM (objs)); if (rc < 0) { log_error ("private keys enumeration failed: %s\n", sc_strerror (rc)); return GNUPG_Card_Error; } nobjs = rc; rc = 0; if (idx >= nobjs) return -1; pinfo = 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 = GNUPG_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 GNUPG_Card_Error; } cert = ksba_cert_new (); if (!cert) { sc_pkcs15_free_certificate (certder); return GNUPG_Out_Of_Core; } 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 GNUPG_Card_Error; } 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 GNUPG_Card_Error; } 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 GNUPG_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; } 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 GNUPG_Invalid_Id; for (s=idstr+9, n=0; hexdigitp (s); s++, n++) ; if (*s || (n&1)) return GNUPG_Invalid_Id; /* invalid or odd number of digits */ n /= 2; if (!n || n > SC_PKCS15_MAX_ID_SIZE) return GNUPG_Invalid_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 GNUPG_Invalid_Value; if (!card->p15card) return GNUPG_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 GNUPG_Card_Error; } *cert = xtrymalloc (certder->data_len); if (!*cert) { sc_pkcs15_free_certificate (certder); return GNUPG_Out_Of_Core; } 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 GNUPG_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 GNUPG_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 GNUPG_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, void **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 GNUPG_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 GNUPG_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 = GNUPG_Card_Error; } 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, void **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 GNUPG_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 = GNUPG_Card_Error; } 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.read_cert = p15_read_cert; card->fnc.sign = p15_sign; card->fnc.decipher = p15_decipher; }