gpg: Improve passphrase caching.
[gnupg.git] / scd / app-dinsig.c
index 54b54b1..7dad6b1 100644 (file)
@@ -1,5 +1,5 @@
 /* app-dinsig.c - The DINSIG (DIN V 66291-1) card application.
- * Copyright (C) 2002, 2004, 2005, 2007 Free Software Foundation, Inc.
+ * Copyright (C) 2002, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -22,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.
@@ -86,7 +86,7 @@
 
 
 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];
@@ -97,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
@@ -126,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)
     {
@@ -141,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;
 }
@@ -158,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.
 */
@@ -177,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);
@@ -205,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);
@@ -233,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. */
@@ -253,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);
@@ -261,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;
@@ -280,21 +282,21 @@ verify_pin (app_t app,
 {
   const char *s;
   int rc;
-  iso7816_pininfo_t pininfo;
+  pininfo_t pininfo;
 
-  if ( app->did_chv1 && !app->force_chv1 ) 
+  if ( app->did_chv1 && !app->force_chv1 )
     return 0;  /* No need to verify it again.  */
 
   memset (&pininfo, 0, sizeof pininfo);
-  pininfo.mode = 1;
+  pininfo.fixedlen = -1;
   pininfo.minlen = 6;
   pininfo.maxlen = 8;
 
-  if (!opt.disable_keypad
-      && !iso7816_check_keypad (app->slot, ISO7816_VERIFY, &pininfo) )
+  if (!opt.disable_pinpad
+      && !iso7816_check_pinpad (app->slot, ISO7816_VERIFY, &pininfo) )
     {
       rc = pincb (pincb_arg,
-                  _("||Please enter your PIN at the reader's keypad"),
+                  _("||Please enter your PIN at the reader's pinpad"),
                   NULL);
       if (rc)
         {
@@ -302,11 +304,11 @@ verify_pin (app_t app,
                     gpg_strerror (rc));
           return rc;
         }
-      rc = iso7816_verify_kp (app->slot, 0x81, "", 0, &pininfo); 
+      rc = iso7816_verify_kp (app->slot, 0x81, &pininfo);
       /* Dismiss the prompt. */
       pincb (pincb_arg, NULL, NULL);
     }
-  else  /* No Keypad.  */
+  else  /* No Pinpad.  */
     {
       char *pinvalue;
 
@@ -353,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;
@@ -384,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,
@@ -397,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);
@@ -421,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
@@ -430,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. */
@@ -461,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)
     {
@@ -475,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;