/* 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
 */

/* The German signature law and its bylaw (SigG and SigV) is currently
   used with an interface specification described in DIN V 66291-1.
   The AID to be used is: 'D27600006601'.

   The file IDs for certificates utilize the generic format: 
        Cxyz
    C being the hex digit 'C' (12).
    x being the service indicator:
         '0' := SigG conform digital signature.
         '1' := entity authentication.
         '2' := key encipherment.
         '3' := data encipherment.
         '4' := key agreement.
         other values are reserved for future use.
    y being the security environment number using '0' for cards
      not supporting a SE number.
    z being the certificate type:
         '0'        := C.CH (base certificate of ard holder) or C.ICC.
         '1' .. '7' := C.CH (business or professional certificate
                       of card holder.
         '8' .. 'D' := C.CA (certificate of a CA issue by the Root-CA).
         'E'        := C.RCA (self certified certificate of the Root-CA).
         'F'        := reserved.
   
   The file IDs used by default are:
   '1F00'  EF.SSD (security service descriptor). [o,o]
   '2F02'  EF.GDO (global data objects) [m,m]
   'A000'  EF.PROT (signature log).  Cyclic file with 20 records of 53 byte.
           Read and update after user authentication. [o,o]
   'B000'  EF.PK.RCA.DS (public keys of Root-CA).  Size is 512b or size 
           of keys. [m (unless a 'C00E' is present),m]
   'B001'  EF.PK.CA.DS (public keys of CAs).  Size is 512b or size
           of keys. [o,o]
   'C00n'  EF.C.CH.DS (digital signature certificate of card holder)
           with n := 0 .. 7.  Size is 2k or size of cert.  Read and
           update allowed after user authentication. [m,m]
   'C00m'  EF.C.CA.DS (digital signature certificate of CA)
           with m := 8 .. E.  Size is 1k or size of cert.  Read always 
           allowed, update after uder authentication. [o,o]
   'C100'  EF.C.ICC.AUT (AUT certificate of ICC) [o,m]
   'C108'  EF.C.CA.AUT (AUT certificate of CA) [o,m]
   'D000'  EF.DM (display message) [-,m]
   
   The letters in brackets indicate optional or mandatory files: The
   first for card terminals under full control and the second for
   "business" card terminals.

   FIXME: Needs a lot more explanation.

*/


#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"

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)
    {
      gpg_error_t tmperr = out_of_core ();
      xfree (buf);
      return tmperr;
    }

  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 gpg_error (GPG_ERR_CARD);
    }
  if (card_help_get_keygrip (cert, keygrip))
    {
      log_error ("failed to calculate the keygrip at index %d\n", idx);
      ksba_cert_release (cert);
      return gpg_error (GPG_ERR_CARD);
    }      
  ksba_cert_release (cert);

  /* return the iD */
  if (keyid)
    {
      *keyid = xtrymalloc (17);
      if (!*keyid)
        return 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 gpg_error (GPG_ERR_INV_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 gpg_error (GPG_ERR_CARD);
    }
  if (file->size < 20) /* check against a somewhat arbitrary length */
    { 
      log_error ("certificate EF too short\n");
      sc_file_free (file);
      return gpg_error (GPG_ERR_CARD);
    }
  buf = xtrymalloc (file->size);
  if (!buf)
    {
      gpg_error_t tmperr = out_of_core ();
      sc_file_free (file);
      return tmperr;
    }
      
  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 gpg_error (GPG_ERR_CARD);
    }
  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;

}
#endif /*HAVE_OPENSC*/
