diff options
author | Werner Koch <wk@gnupg.org> | 2002-03-18 10:42:03 +0100 |
---|---|---|
committer | Werner Koch <wk@gnupg.org> | 2002-03-18 10:42:03 +0100 |
commit | 2e553c177745c3ab473f56d8c9cce6c1832eac4d (patch) | |
tree | a0ce3143caa935a140699d4eecb2901bf4bc4d17 /scd/card-p15.c | |
parent | Doc fixes, moved some fizmes to TODO, fixed minor bugs. (diff) | |
download | gnupg2-2e553c177745c3ab473f56d8c9cce6c1832eac4d.tar.xz gnupg2-2e553c177745c3ab473f56d8c9cce6c1832eac4d.zip |
More stuff for SCs - don't expect them to work.
Diffstat (limited to 'scd/card-p15.c')
-rw-r--r-- | scd/card-p15.c | 399 |
1 files changed, 399 insertions, 0 deletions
diff --git a/scd/card-p15.c b/scd/card-p15.c new file mode 100644 index 000000000..950508fdb --- /dev/null +++ b/scd/card-p15.c @@ -0,0 +1,399 @@ +/* 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> + +#include <opensc-pkcs15.h> +#include <ksba.h> + +#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; +} + + + +/* 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 = 0; + struct sc_pkcs15_id keyid; + struct sc_pkcs15_pin_info *pin; + struct sc_pkcs15_object *keyobj, *pinobj; + char *pinvalue; + int rc; + unsigned char *outbuf = NULL; + size_t outbuflen; + + if (hashalgo != GCRY_MD_SHA1) + return GNUPG_Unsupported_Algorithm; + + 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)); + rc = GNUPG_No_Secret_Key; + goto leave; + } + rc = 0; + + 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)); + rc = GNUPG_Bad_PIN_Method; + goto leave; + } + 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)); + goto leave; + } + + 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)); + rc = GNUPG_Bad_PIN; + goto leave; + } + +/* cryptflags |= SC_PKCS15_HASH_SHA1; */ +/* cryptflags |= SC_PKCS15_PAD_PKCS1_V1_5; */ + + 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; + } + + +leave: + 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_id keyid; + struct sc_pkcs15_pin_info *pin; + struct sc_pkcs15_object *keyobj, *pinobj; + char *pinvalue; + int rc; + unsigned char *outbuf = NULL; + size_t outbuflen; + + 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)); + rc = GNUPG_No_Secret_Key; + goto leave; + } + rc = 0; + + 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)); + rc = GNUPG_Bad_PIN_Method; + goto leave; + } + 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)); + goto leave; + } + + 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)); + rc = GNUPG_Bad_PIN; + goto leave; + } + + outbuflen = indatalen < 256? 256 : indatalen; + outbuf = xtrymalloc (outbuflen); + if (!outbuf) + return GNUPG_Out_Of_Core; + + /* OpenSC does not yet support decryption for cryptflex cards */ +/* rc = sc_pkcs15_decipher (card->p15card, key, */ +/* indata, indatalen, */ +/* outbuf, outbuflen); */ + rc = sc_pkcs15_compute_signature (card->p15card, keyobj, + 0, + indata, indatalen, + outbuf, outbuflen ); + if (rc < 0) + { + log_error ("failed to decipger the data: %s\n", sc_strerror (rc)); + rc = GNUPG_Card_Error; + } + else + { + *outdatalen = rc; + *outdata = outbuf; + outbuf = NULL; + rc = 0; + } + + +leave: + 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; +} |