gpg: Improve passphrase caching.
[gnupg.git] / scd / app-dinsig.c
index 5cab428..7dad6b1 100644 (file)
@@ -1,11 +1,11 @@
 /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
- *     Copyright (C) 2002, 2004, 2005 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2005, 2007, 2008 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
+ * the Free Software Foundation; either version 3 of the License, or
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,9 +14,7 @@
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 
@@ -24,7 +22,7 @@
    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: 
+   The file IDs for certificates utilize the generic format:
         Cxyz
     C being the hex digit 'C' (12).
     x being the service indicator:
          '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 
+   '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]
            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 
+           with m := 8 .. E.  Size is 1k or size of cert.  Read always
            allowed, update after user 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.
 
 #include "scdaemon.h"
 
+#include "i18n.h"
 #include "iso7816.h"
 #include "app-common.h"
 #include "tlv.h"
 
 
 static gpg_error_t
-do_learn_status (app_t app, ctrl_t ctrl)
+do_learn_status (app_t app, ctrl_t ctrl, unsigned int flags)
 {
   gpg_error_t err;
   char ct_buf[100], id_buf[100];
@@ -98,17 +97,19 @@ do_learn_status (app_t app, ctrl_t ctrl)
   ksba_cert_t cert;
   int fid;
 
+  (void)flags;
+
   /* Return the certificate of the card holder. */
   fid = 0xC000;
-  len = app_help_read_length_of_cert (app->slot, fid, &certoff); 
+  len = app_help_read_length_of_cert (app->slot, fid, &certoff);
   if (!len)
     return 0; /* Card has not been personalized. */
 
   sprintf (ct_buf, "%d", 101);
   sprintf (id_buf, "DINSIG.%04X", fid);
   send_status_info (ctrl, "CERTINFO",
-                    ct_buf, strlen (ct_buf), 
-                    id_buf, strlen (id_buf), 
+                    ct_buf, strlen (ct_buf),
+                    id_buf, strlen (id_buf),
                     NULL, (size_t)0);
 
   /* Now we need to read the certificate, so that we can get the
@@ -127,7 +128,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
       xfree (der);
       return err;
     }
-  err = ksba_cert_init_from_mem (cert, der, derlen); 
+  err = ksba_cert_init_from_mem (cert, der, derlen);
   xfree (der); der = NULL;
   if (err)
     {
@@ -142,13 +143,13 @@ do_learn_status (app_t app, ctrl_t ctrl)
       log_error ("failed to calculate the keygrip for FID 0x%04X\n", fid);
       ksba_cert_release (cert);
       return gpg_error (GPG_ERR_CARD);
-    }      
+    }
   ksba_cert_release (cert);
 
   sprintf (id_buf, "DINSIG.%04X", fid);
   send_status_info (ctrl, "KEYPAIRINFO",
-                    hexkeygrip, 40, 
-                    id_buf, strlen (id_buf), 
+                    hexkeygrip, 40,
+                    id_buf, strlen (id_buf),
                     NULL, (size_t)0);
   return 0;
 }
@@ -159,7 +160,7 @@ do_learn_status (app_t app, ctrl_t ctrl)
 /* Read the certificate with id CERTID (as returned by learn_status in
    the CERTINFO status lines) and return it in the freshly allocated
    buffer put into CERT and the length of the certificate put into
-   CERTLEN. 
+   CERTLEN.
 
    FIXME: This needs some cleanups and caching with do_learn_status.
 */
@@ -178,11 +179,11 @@ do_readcert (app_t app, const char *certid,
 
   *cert = NULL;
   *certlen = 0;
-  if (strncmp (certid, "DINSIG.", 7) ) 
+  if (strncmp (certid, "DINSIG.", 7) )
     return gpg_error (GPG_ERR_INV_ID);
   certid += 7;
   if (!hexdigitp (certid) || !hexdigitp (certid+1)
-      || !hexdigitp (certid+2) || !hexdigitp (certid+3) 
+      || !hexdigitp (certid+2) || !hexdigitp (certid+3)
       || certid[4])
     return gpg_error (GPG_ERR_INV_ID);
   fid = xtoi_4 (certid);
@@ -206,7 +207,7 @@ do_readcert (app_t app, const char *certid,
                  fid, gpg_strerror (err));
       return err;
     }
-  
+
   if (!buflen || *buffer == 0xff)
     {
       log_info ("no certificate contained in FID 0x%04X\n", fid);
@@ -234,13 +235,13 @@ do_readcert (app_t app, const char *certid,
                           &ndef, &objlen, &hdrlen);
   if (err)
     goto leave;
-  
+
   if (rootca)
     ;
   else if (class == CLASS_UNIVERSAL && tag == TAG_OBJECT_ID && !constructed)
     {
       const unsigned char *save_p;
-  
+
       /* The certificate seems to be contained in a userCertificate
          container.  Skip this and assume the following sequence is
          the certificate. */
@@ -254,7 +255,7 @@ do_readcert (app_t app, const char *certid,
       save_p = p;
       err = parse_ber_header (&p, &n, &class, &tag, &constructed,
                               &ndef, &objlen, &hdrlen);
-      if (err) 
+      if (err)
         goto leave;
       if ( !(class == CLASS_UNIVERSAL && tag == TAG_SEQUENCE && constructed) )
         return gpg_error (GPG_ERR_INV_OBJ);
@@ -262,7 +263,7 @@ do_readcert (app_t app, const char *certid,
       assert (save_p + totobjlen <= buffer + buflen);
       memmove (buffer, save_p, totobjlen);
     }
-  
+
   *cert = buffer;
   buffer = NULL;
   *certlen = totobjlen;
@@ -279,11 +280,37 @@ verify_pin (app_t app,
             gpg_error_t (*pincb)(void*, const char *, char **),
             void *pincb_arg)
 {
-  if (!app->did_chv1 || app->force_chv1 ) 
+  const char *s;
+  int rc;
+  pininfo_t pininfo;
+
+  if ( app->did_chv1 && !app->force_chv1 )
+    return 0;  /* No need to verify it again.  */
+
+  memset (&pininfo, 0, sizeof pininfo);
+  pininfo.fixedlen = -1;
+  pininfo.minlen = 6;
+  pininfo.maxlen = 8;
+
+  if (!opt.disable_pinpad
+      && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
+    {
+      rc = pincb (pincb_arg,
+                  _("||Please enter your PIN at the reader's pinpad"),
+                  NULL);
+      if (rc)
+        {
+          log_info (_("PIN callback returned error: %s\n"),
+                    gpg_strerror (rc));
+          return rc;
+        }
+      rc = iso7816_verify_kp (app->slot, 0x81, &pininfo);
+      /* Dismiss the prompt. */
+      pincb (pincb_arg, NULL, NULL);
+    }
+  else  /* No Pinpad.  */
     {
-      const char *s;
       char *pinvalue;
-      int rc;
 
       rc = pincb (pincb_arg, "PIN", &pinvalue);
       if (rc)
@@ -303,15 +330,17 @@ verify_pin (app_t app,
           return gpg_error (GPG_ERR_BAD_PIN);
         }
 
-      if (strlen (pinvalue) < 6)
+      if (strlen (pinvalue) < pininfo.minlen)
         {
-          log_error ("PIN is too short; minimum length is 6\n");
+          log_error ("PIN is too short; minimum length is %d\n",
+                     pininfo.minlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
-      else if (strlen (pinvalue) > 8)
+      else if (strlen (pinvalue) > pininfo.maxlen)
         {
-          log_error ("PIN is too large; maximum length is 8\n");
+          log_error ("PIN is too large; maximum length is %d\n",
+                     pininfo.maxlen);
           xfree (pinvalue);
           return gpg_error (GPG_ERR_BAD_PIN);
         }
@@ -336,19 +365,18 @@ verify_pin (app_t app,
           if (i < sizeof paddedpin && *s)
             paddedpin[i++] = (((*s - '0') << 4) | 0x0f);
           while (i < sizeof paddedpin)
-              paddedpin[i++] = 0xff;
+            paddedpin[i++] = 0xff;
           rc = iso7816_verify (app->slot, 0x81, paddedpin, sizeof paddedpin);
         }
-      if (rc)
-        {
-          log_error ("verify PIN failed\n");
-          xfree (pinvalue);
-          return rc;
-        }
-      app->did_chv1 = 1;
       xfree (pinvalue);
     }
 
+  if (rc)
+    {
+      log_error ("verify PIN failed\n");
+      return rc;
+    }
+  app->did_chv1 = 1;
   return 0;
 }
 
@@ -358,7 +386,7 @@ verify_pin (app_t app,
    If a PIN is required the PINCB will be used to ask for the PIN;
    that callback should return the PIN in an allocated buffer and
    store that in the 3rd argument.  */
-static gpg_error_t 
+static gpg_error_t
 do_sign (app_t app, const char *keyidstr, int hashalgo,
          gpg_error_t (*pincb)(void*, const char *, char **),
          void *pincb_arg,
@@ -371,23 +399,29 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
     { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
       0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+  static unsigned char sha256_prefix[19] = /* OID is 2.16.840.1.101.3.4.2.1 */
+    { 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86,
+      0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05,
+      0x00, 0x04, 0x20 };
   int rc;
   int fid;
-  unsigned char data[35];   /* Must be large enough for a SHA-1 digest
-                               + the largest OID _prefix above. */
+  unsigned char data[19+32]; /* Must be large enough for a SHA-256 digest
+                                + the largest OID _prefix above. */
+  int datalen;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
-  if (indatalen != 20 && indatalen != 16 && indatalen != 35)
+  if (indatalen != 20 && indatalen != 16 && indatalen != 32
+      && indatalen != (15+20) && indatalen != (19+32))
     return gpg_error (GPG_ERR_INV_VALUE);
 
   /* Check that the provided ID is vaid.  This is not really needed
      but we do it to to enforce correct usage by the caller. */
-  if (strncmp (keyidstr, "DINSIG.", 7) ) 
+  if (strncmp (keyidstr, "DINSIG.", 7) )
     return gpg_error (GPG_ERR_INV_ID);
   keyidstr += 7;
   if (!hexdigitp (keyidstr) || !hexdigitp (keyidstr+1)
-      || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3) 
+      || !hexdigitp (keyidstr+2) || !hexdigitp (keyidstr+3)
       || keyidstr[4])
     return gpg_error (GPG_ERR_INV_ID);
   fid = xtoi_4 (keyidstr);
@@ -395,7 +429,8 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
     return gpg_error (GPG_ERR_NOT_FOUND);
 
   /* Prepare the DER object from INDATA. */
-  if (indatalen == 35)
+  datalen = 35;
+  if (indatalen == 15+20)
     {
       /* Alright, the caller was so kind to send us an already
          prepared DER object.  Check that it is what we want and that
@@ -404,28 +439,108 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
         ;
       else if (hashalgo == GCRY_MD_RMD160 && !memcmp (indata, rmd160_prefix,15))
         ;
-      else 
+      else
+        return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+      memcpy (data, indata, indatalen);
+    }
+  else if (indatalen == 19+32)
+    {
+      /* Alright, the caller was so kind to send us an already
+         prepared DER object.  Check that it is what we want and that
+         it matches the hash algorithm. */
+      datalen = indatalen;
+      if (hashalgo == GCRY_MD_SHA256 && !memcmp (indata, sha256_prefix, 19))
+        ;
+      else if (hashalgo == GCRY_MD_SHA1 && !memcmp (indata, sha256_prefix, 19))
+        {
+          /* Fixme: This is a kludge.  A better solution is not to use
+             SHA1 as default but use an autodetection.  However this
+             needs changes in all app-*.c */
+          hashalgo = GCRY_MD_SHA256;
+          datalen  = indatalen;
+        }
+      else
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
       memcpy (data, indata, indatalen);
     }
   else
     {
+      int len = 15;
       if (hashalgo == GCRY_MD_SHA1)
-        memcpy (data, sha1_prefix, 15);
+        memcpy (data, sha1_prefix, len);
       else if (hashalgo == GCRY_MD_RMD160)
-        memcpy (data, rmd160_prefix, 15);
-      else 
+        memcpy (data, rmd160_prefix, len);
+      else if (hashalgo == GCRY_MD_SHA256)
+        {
+          len = 19;
+          datalen = len + indatalen;
+          memcpy (data, sha256_prefix, len);
+        }
+      else
         return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
-      memcpy (data+15, indata, indatalen);
+      memcpy (data+len, indata, indatalen);
     }
 
   rc = verify_pin (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+    rc = iso7816_compute_ds (app->slot, 0, data, datalen, 0,
+                             outdata, outdatalen);
   return rc;
 }
 
 
+#if 0
+#warning test function - works but may brick your card
+/* Handle the PASSWD command.  CHVNOSTR is currently ignored; we
+   always use VHV0.  RESET_MODE is not yet implemented.  */
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl,  const char *chvnostr,
+               unsigned int flags,
+               gpg_error_t (*pincb)(void*, const char *, char **),
+               void *pincb_arg)
+{
+  gpg_error_t err;
+  char *pinvalue;
+  const char *oldpin;
+  size_t oldpinlen;
+
+  if ((flags & APP_CHANGE_FLAG_RESET))
+    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+
+  if ((flags & APP_CHANGE_FLAG_NULLPIN))
+    {
+      /* With the nullpin flag, we do not verify the PIN - it would fail
+         if the Nullpin is still set.  */
+      oldpin = "\0\0\0\0\0";
+      oldpinlen = 6;
+    }
+  else
+    {
+      err = verify_pin (app, pincb, pincb_arg);
+      if (err)
+        return err;
+      oldpin = NULL;
+      oldpinlen = 0;
+    }
+
+  /* TRANSLATORS: Do not translate the "|*|" prefixes but
+     keep it at the start of the string.  We need this elsewhere
+     to get some infos on the string. */
+  err = pincb (pincb_arg, _("|N|Initial New PIN"), &pinvalue);
+  if (err)
+    {
+      log_error (_("error getting new PIN: %s\n"), gpg_strerror (err));
+      return err;
+    }
+
+  err = iso7816_change_reference_data (app->slot, 0x81,
+                                       oldpin, oldpinlen,
+                                       pinvalue, strlen (pinvalue));
+  xfree (pinvalue);
+  return err;
+}
+#endif /*0*/
+
 
 /* Select the DINSIG application on the card in SLOT.  This function
    must be used before any other DINSIG application functions. */
@@ -435,7 +550,7 @@ app_select_dinsig (app_t app)
   static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
   int slot = app->slot;
   int rc;
-  
+
   rc = iso7816_select_application (slot, aid, sizeof aid, 0);
   if (!rc)
     {
@@ -449,7 +564,7 @@ app_select_dinsig (app_t app)
       app->fnc.sign = do_sign;
       app->fnc.auth = NULL;
       app->fnc.decipher = NULL;
-      app->fnc.change_pin = NULL;
+      app->fnc.change_pin = NULL /*do_change_pin*/;
       app->fnc.check_pin = NULL;
 
       app->force_chv1 = 1;