diff options
-rw-r--r-- | scd/card-common.h | 67 | ||||
-rw-r--r-- | scd/card-dinsig.c | 205 | ||||
-rw-r--r-- | scd/card-p15.c | 399 |
3 files changed, 671 insertions, 0 deletions
diff --git a/scd/card-common.h b/scd/card-common.h new file mode 100644 index 000000000..1c912b744 --- /dev/null +++ b/scd/card-common.h @@ -0,0 +1,67 @@ +/* card-common.h - Common declarations for all card types + * Copyright (C) 2001, 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 + */ + +#ifndef CARD_COMMON_H +#define CARD_COMMON_H + + +struct card_ctx_s { + int reader; /* used reader */ + struct sc_context *ctx; + struct sc_card *scard; + struct sc_pkcs15_card *p15card; /* only if there is a pkcs15 application */ + + struct { + int initialized; /* the card has been initialied and the function + pointers may be used. However for + unsupported operations the particular + function pointer is set to NULL */ + + int (*enum_keypairs) (CARD card, int idx, + unsigned char *keygrip, char **keyid); + int (*read_cert) (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert); + int (*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 ); + int (*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); + } fnc; + +}; + +/*-- card.c --*/ +int map_sc_err (int rc); +int card_help_get_keygrip (KsbaCert cert, unsigned char *array); + + + +/* constructors */ +void card_p15_bind (CARD card); +void card_dinsig_bind (CARD card); + + +#endif /*CARD_COMMON_H*/ diff --git a/scd/card-dinsig.c b/scd/card-dinsig.c new file mode 100644 index 000000000..3e461fd99 --- /dev/null +++ b/scd/card-dinsig.c @@ -0,0 +1,205 @@ +/* card-dinsig.c - German signature law (DINSIG) functions + * 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" + +static int dinsig_read_cert (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert); + + + +/* See card.c for interface description. Frankly we don't do any real + enumeration but just check whether the well know files are + available. + */ +static int +dinsig_enum_keypairs (CARD card, int idx, + unsigned char *keygrip, char **keyid) +{ + int rc; + unsigned char *buf; + size_t buflen; + KsbaError krc; + KsbaCert cert; + + /* fixme: We should locate the application via the EF(DIR) and not + assume a Netkey card */ + if (!idx) + rc = dinsig_read_cert (card, "DINSIG-DF01.C000", &buf, &buflen); + else if (idx == 1) + rc = dinsig_read_cert (card, "DINSIG-DF01.C200", &buf, &buflen); + else + rc = -1; + if (rc) + return rc; + + cert = ksba_cert_new (); + if (!cert) + { + xfree (buf); + return GNUPG_Out_Of_Core; + } + + krc = ksba_cert_init_from_mem (cert, buf, buflen); + xfree (buf); + if (krc) + { + log_error ("failed to parse the certificate at idx %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 at index %d\n", idx); + ksba_cert_release (cert); + return GNUPG_Card_Error; + } + ksba_cert_release (cert); + + /* return the iD */ + if (keyid) + { + *keyid = xtrymalloc (17); + if (!*keyid) + return GNUPG_Out_Of_Core; + if (!idx) + strcpy (*keyid, "DINSIG-DF01.C000"); + else + strcpy (*keyid, "DINSIG-DF01.C200"); + } + + return 0; +} + + + +/* See card.c for interface description */ +static int +dinsig_read_cert (CARD card, const char *certidstr, + unsigned char **cert, size_t *ncert) +{ + int rc; + struct sc_path path; + struct sc_file *file; + unsigned char *buf; + int buflen; + + if (!strcmp (certidstr, "DINSIG-DF01.C000")) + sc_format_path ("3F00DF01C000", &path); + else if (!strcmp (certidstr, "DINSIG-DF01.C200")) + sc_format_path ("3F00DF01C200", &path); + else + return GNUPG_Invalid_Id; + + rc = sc_select_file (card->scard, &path, &file); + if (rc) + { + log_error ("sc_select_file failed: %s\n", sc_strerror (rc)); + return map_sc_err (rc); + } + if (file->type != SC_FILE_TYPE_WORKING_EF + || file->ef_structure != SC_FILE_EF_TRANSPARENT) + { + log_error ("wrong type or structure of certificate EF\n"); + sc_file_free (file); + return GNUPG_Card_Error; + } + if (file->size < 20) /* check against a somewhat arbitrary length */ + { + log_error ("certificate EF too short\n"); + sc_file_free (file); + return GNUPG_Card_Error; + } + buf = xtrymalloc (file->size); + if (!buf) + { + sc_file_free (file); + return GNUPG_Out_Of_Core; + } + + rc = sc_read_binary (card->scard, 0, buf, file->size, 0); + if (rc >= 0 && rc != file->size) + { + log_error ("short read on certificate EF\n"); + sc_file_free (file); + xfree (buf); + return GNUPG_Card_Error; + } + sc_file_free (file); + if (rc < 0) + { + log_error ("error reading certificate EF: %s\n", sc_strerror (rc)); + xfree (buf); + return map_sc_err (rc); + } + buflen = rc; + + /* The object is not a plain certificate but wrapped into id-at + userCertificate - fixme: we should check the specs and decided + whether libksba should support it */ + if (buflen > 9 && buf[0] == 0x30 && buf[4] == 6 && buf[5] == 3 + && buf[6] == 0x55 && buf[7] == 4 && buf[8] == 0x24) + { + /* We have to strip the padding. Although this is a good idea + anyway, we have to do it due to a KSBA problem; KSBA does not + work correct when the buffer is larger than the ASN.1 + structure and the certificates here are padded with FF. So + as a workaround we look at the outer structure to get the + size of the entire thing and adjust the buflen. We can only + do this when there is a 2 byte length field */ + size_t seqlen; + if (buf[1] == 0x82) + { + seqlen = ((buf[2] << 8) | buf[3]) + 4; + if (seqlen < buflen) + buflen = seqlen; + } + memmove (buf, buf+9, buflen-9); + buflen -= 9; + } + + *cert = buf; + *ncert = buflen; + return 0; +} + + + + +/* Bind our operations to the card */ +void +card_dinsig_bind (CARD card) +{ + card->fnc.enum_keypairs = dinsig_enum_keypairs; + card->fnc.read_cert = dinsig_read_cert; + +} 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; +} |