diff options
Diffstat (limited to 'scd/card-dinsig.c')
-rw-r--r-- | scd/card-dinsig.c | 205 |
1 files changed, 205 insertions, 0 deletions
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; + +} |