Fix for extended length Le in decipher
[gnupg.git] / scd / app-openpgp.c
index 9620d5b..d468591 100644 (file)
@@ -16,8 +16,6 @@
  *
  * You should have received a copy of the GNU General Public License
  * along with this program; if not, see <http://www.gnu.org/licenses/>.
- *
- * $Id$
  */
 
 /* Some notes:
@@ -214,6 +212,11 @@ static gpg_error_t do_auth (app_t app, const char *keyidstr,
                             const void *indata, size_t indatalen,
                             unsigned char **outdata, size_t *outdatalen);
 static void parse_algorithm_attribute (app_t app, int keyno);
+static gpg_error_t change_keyattr_from_string
+                           (app_t app, 
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *value, size_t valuelen);
 
 
 
@@ -1793,6 +1796,7 @@ do_setattr (app_t app, const char *name,
     { "CERT-3",       0x7F21, 3, 0, 1 },
     { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
     { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
+    { "KEY-ATTR",     0,      0, 3, 1 },
     { NULL, 0 }
   };
   int exmode;
@@ -1804,6 +1808,9 @@ do_setattr (app_t app, const char *name,
   if (table[idx].need_v2 && !app->app_local->extcap.is_v2)
     return gpg_error (GPG_ERR_NOT_SUPPORTED); /* Not yet supported.  */
 
+  if (table[idx].special == 3)
+    return change_keyattr_from_string (app, pincb, pincb_arg, value, valuelen);
+
   switch (table[idx].need_chv)
     {
     case 2:
@@ -2397,10 +2404,52 @@ change_keyattr (app_t app, int keyno, unsigned int nbits,
     log_info ("size of key %d changed to %u bits\n", keyno+1, nbits);
   flush_cache (app);
   parse_algorithm_attribute (app, keyno);
+  app->did_chv1 = 0;
+  app->did_chv2 = 0;
+  app->did_chv3 = 0;
   return err;
 }
 
 
+/* Helper to process an setattr command for name KEY-ATTR.  It expects
+   a string "--force <keyno> <algo> <nbits>" in (VALUE,VALUELEN).  */
+static gpg_error_t 
+change_keyattr_from_string (app_t app, 
+                            gpg_error_t (*pincb)(void*, const char *, char **),
+                            void *pincb_arg,
+                            const void *value, size_t valuelen)
+{
+  gpg_error_t err;
+  char *string;
+  int keyno, algo;
+  unsigned int nbits;
+
+  /* VALUE is expected to be a string but not guaranteed to be
+     terminated.  Thus copy it to an allocated buffer first. */
+  string = xtrymalloc (valuelen+1);
+  if (!string)
+    return gpg_error_from_syserror ();
+  memcpy (string, value, valuelen);
+  string[valuelen] = 0;
+
+  /* Because this function deletes the key we require the string
+     "--force" in the data to make clear that something serious might
+     happen.  */
+  if (sscanf (string, " --force %d %d %u", &keyno, &algo, &nbits) != 3)
+    err = gpg_error (GPG_ERR_INV_DATA);
+  else if (keyno < 1 || keyno > 3)
+    err = gpg_error (GPG_ERR_INV_ID);
+  else if (algo != 1)
+    err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Not RSA.  */
+  else if (nbits < 1024)
+    err = gpg_error (GPG_ERR_TOO_SHORT);
+  else
+    err = change_keyattr (app, keyno-1, nbits, pincb, pincb_arg);
+
+  xfree (string);
+  return err;
+}
+
 
 /* Handle the WRITEKEY command for OpenPGP.  This function expects a
    canonical encoded S-expression with the secret key in KEYDATA and
@@ -3008,6 +3057,7 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
   const char *fpr = NULL;
   unsigned long sigcount;
   int use_auth = 0;
+  int exmode, le_value;
 
   if (!keyidstr || !*keyidstr)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -3148,7 +3198,19 @@ do_sign (app_t app, const char *keyidstr, int hashalgo,
       xfree (pinvalue);
     }
 
-  rc = iso7816_compute_ds (app->slot, data, datalen, outdata, outdatalen);
+
+  if (app->app_local->cardcap.ext_lc_le)
+    {
+      exmode = 1;    /* Use extended length.  */
+      le_value = app->app_local->extcap.max_rsp_data;
+    }
+  else
+    {
+      exmode = 0;
+      le_value = 0; 
+    }
+  rc = iso7816_compute_ds (app->slot, exmode, data, datalen, le_value,
+                           outdata, outdatalen);
   return rc;
 }
 
@@ -3219,8 +3281,23 @@ do_auth (app_t app, const char *keyidstr,
 
   rc = verify_chv2 (app, pincb, pincb_arg);
   if (!rc)
-    rc = iso7816_internal_authenticate (app->slot, indata, indatalen,
-                                        outdata, outdatalen);
+    {
+      int exmode, le_value;
+
+      if (app->app_local->cardcap.ext_lc_le)
+        {
+          exmode = 1;    /* Use extended length.  */
+          le_value = app->app_local->extcap.max_rsp_data;
+        }
+      else
+        {
+          exmode = 0;
+          le_value = 0; 
+        }
+      rc = iso7816_internal_authenticate (app->slot, exmode,
+                                          indata, indatalen, le_value,
+                                          outdata, outdatalen);
+    }
   return rc;
 }
 
@@ -3237,7 +3314,7 @@ do_decipher (app_t app, const char *keyidstr,
   const char *s;
   int n;
   const char *fpr = NULL;
-  int exmode;
+  int exmode, le_value;
 
   if (!keyidstr || !*keyidstr || !indatalen)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -3320,16 +3397,22 @@ do_decipher (app_t app, const char *keyidstr,
           indatalen = fixuplen + indatalen;
           padind = -1; /* Already padded.  */
         }
-      
+
       if (app->app_local->cardcap.ext_lc_le && indatalen > 254 )
-        exmode = 1;    /* Extended length w/o a limit.  */
+        {
+          exmode = 1;    /* Extended length w/o a limit.  */
+          le_value = app->app_local->extcap.max_rsp_data;
+        }
       else if (app->app_local->cardcap.cmd_chaining && indatalen > 254)
-        exmode = -254; /* Command chaining with max. 254 bytes.  */
+        {
+          exmode = -254; /* Command chaining with max. 254 bytes.  */
+          le_value = 0;
+        }
       else
-        exmode = 0;    
+        exmode = le_value = 0;    
 
       rc = iso7816_decipher (app->slot, exmode, 
-                             indata, indatalen, padind,
+                             indata, indatalen, le_value, padind,
                              outdata, outdatalen);
       xfree (fixbuf);
     }