w32: Add icons and version information.
[gnupg.git] / scd / app-dinsig.c
index 752e8a3..1a0cb60 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/>.
  */
 
 
 
 #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,6 +97,8 @@ 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); 
@@ -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);
         }
@@ -326,7 +355,7 @@ verify_pin (app_t app,
              this. */
           char paddedpin[8];
           int i, ndigits;
-
+          
           for (ndigits=0, s=pinvalue; *s; ndigits++, s++)
             ;
           i = 0;
@@ -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;
 }
 
@@ -371,14 +399,20 @@ 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
@@ -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
@@ -408,29 +443,109 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
         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);
+        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. */
 gpg_error_t
-app_select_dinsig (APP app)
+app_select_dinsig (app_t app)
 {
   static char const aid[] = { 0xD2, 0x76, 0x00, 0x00, 0x66, 0x01 };
   int slot = app->slot;
@@ -449,7 +564,7 @@ app_select_dinsig (APP 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;