[svn] GnuPG - r4835 - in trunk: . agent g10 scd

svn author wk cvs at cvs.gnupg.org
Thu Sep 25 12:06:03 CEST 2008


Author: wk
Date: 2008-09-25 12:06:02 +0200 (Thu, 25 Sep 2008)
New Revision: 4835

Modified:
   trunk/NEWS
   trunk/agent/ChangeLog
   trunk/agent/divert-scd.c
   trunk/g10/ChangeLog
   trunk/g10/call-agent.c
   trunk/g10/call-agent.h
   trunk/g10/card-util.c
   trunk/scd/ChangeLog
   trunk/scd/app-openpgp.c
   trunk/scd/iso7816.c
   trunk/scd/iso7816.h
Log:
Finished support for v2 cards with the exception of secure messaging.


Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/agent/ChangeLog	2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,7 @@
+2008-09-25  Werner Koch  <wk at g10code.com>
+
+	* divert-scd.c (getpin_cb): Support a Reset Code style PINs..
+
 2008-09-03  Werner Koch  <wk at g10code.com>
 
 	* command.c (parse_keygrip): Use hex2bin.

Modified: trunk/g10/ChangeLog
===================================================================
--- trunk/g10/ChangeLog	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/ChangeLog	2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,16 @@
+2008-09-25  Werner Koch  <wk at g10code.com>
+
+	* card-util.c (change_pin): Support setting of the reset code.
+
+2008-09-24  Werner Koch  <wk at g10code.com>
+
+	* call-agent.h (struct agent_card_info_s): Add field IS_V2.
+	* call-agent.c (learn_status_cb): That that field.
+
+	* card-util.c (change_pin): Rename first arg to UNBLOCK_v2 and use
+	it this way.
+	(card_edit): Add new command UNBLOCK.
+
 2008-09-23  David Shaw  <dshaw at jabberwocky.com>
 
 	* pkclist.c (select_algo_from_prefs): Redo function to rank prefs

Modified: trunk/scd/ChangeLog
===================================================================
--- trunk/scd/ChangeLog	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/ChangeLog	2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,3 +1,22 @@
+2008-09-25  Werner Koch  <wk at g10code.com>
+
+	* app-openpgp.c (do_setattr): Do not allow setting of the reset
+	code.
+	(do_change_pin): Allow setting of the reset code.
+
+2008-09-24  Werner Koch  <wk at g10code.com>
+
+	* app-openpgp.c (verify_chv3): Set the did_chv3 flag which was
+	accidently removed on 2008-03-26.
+	(verify_chv2): Revert last change.
+	(do_change_pin): Do not change CHV2. Add reset code logic for v2
+	cards.
+	* iso7816.c (iso7816_reset_retry_counter_with_rc): New.
+	
+	* app-openpgp.c (add_tlv, build_privkey_template): New.
+	(do_writekey): Support v2 keys and other key lengths than 1024.
+	* iso7816.c (iso7816_put_data_odd): New.
+
 2008-09-23  Werner Koch  <wk at g10code.com>
 
 	* app-openpgp.c (do_sign): Support SHA-2 digests.
@@ -4,6 +23,8 @@
 	(verify_chv2): No CHV auto-sync for v2 cards.
 	(do_auth): Allow 2048 bit keys.
 	(parse_algorithm_attribute): New.
+	(rsa_key_format_t): New.
+	(struct app_local_s): Add struct KEYATTR.
 
 2008-09-23  Marcus Brinkmann  <marcus at g10code.com>
 
@@ -1815,7 +1836,7 @@
 	the gpg-agent.
 
 	
- Copyright 2002, 2003, 2004, 2005, 2007 Free Software Foundation, Inc.
+ Copyright 2002, 2003, 2004, 2005, 2007, 2008 Free Software Foundation, Inc.
 
  This file is free software; as a special exception the author gives
  unlimited permission to copy and/or distribute it, with or without

Modified: trunk/NEWS
===================================================================
--- trunk/NEWS	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/NEWS	2008-09-25 10:06:02 UTC (rev 4835)
@@ -4,32 +4,36 @@
  * New keyserver helper gpg2keys_kdns as generic DNS CERT lookup.  Run
    with --help for a short description.  Requires the ADNS library.
 
- * New mechanisms "local" and "nodefault" for --auto-key-locate [gpg].
+ * [gpg] New mechanisms "local" and "nodefault" for --auto-key-locate.
    Fixed a few problems with this option.
 
- * [W32] Initialized the socket subsystem for all keyserver helpers.
+ * [w32] Initialized the socket subsystem for all keyserver helpers.
 
- * [W32] The sysconf directory has been moved from a subdirectory of
+ * [w32] The sysconf directory has been moved from a subdirectory of
    the installation directory to %CSIDL_COMMON_APPDATA%/GNU/etc/gnupg.
 
- * New gpg2 command --locate-keys.
+ * [gpg] New command --locate-keys.
 
- * New gpg2 options --with-sig-list and --with-sig-check.
+ * [gpg] New options --with-sig-list and --with-sig-check.
 
- * Made gpgsm's --output option work with --export-secret-key-p12.
+ * [gpgsm] Made --output option work with --export-secret-key-p12.
 
  * gpg-connect-agent accepts commands given as command line arguments.
 
- * The gpg2 option --fixed-list-mode is now implicitly used and obsolete.
+ * [gpg] The option --fixed-list-mode is now implicitly used and obsolete.
 
- * New control statement %ask-passphrase for the unattended key
-   generation of gpg2.
+ * [gpg] New control statement %ask-passphrase for the unattended key
+   generation.
 
  * gpgsm now uses AES by default.
 
  * gpg-preset-passphrase works again.
 
+ * Admin PINs are cached again (bug in 2.0.9).
 
+ * Support for version 2 OpenPGP cards.
+
+
 Noteworthy changes in version 2.0.9 (2008-03-26)
 ------------------------------------------------
 

Modified: trunk/agent/divert-scd.c
===================================================================
--- trunk/agent/divert-scd.c	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/agent/divert-scd.c	2008-09-25 10:06:02 UTC (rev 4835)
@@ -185,6 +185,7 @@
             PIN.  If the PIN is not correctly repeated it starts from
             all over.
       'A' = The PIN is an Admin PIN, SO-PIN, PUK or alike.
+      'R' = The PIN is a Reset Code.
 
    Example:
 
@@ -202,6 +203,7 @@
   const char *ends, *s;
   int any_flags = 0;
   int newpin = 0;
+  int resetcode = 0;
   const char *again_text = NULL;
   const char *prompt = "PIN";
 
@@ -217,6 +219,11 @@
             prompt = _("Admin PIN");
           else if (*s == 'N')
             newpin = 1;
+          else if (*s == 'R')
+            {
+              prompt = _("Reset Code");
+              resetcode = 1;
+            }
         }
       info = ends+1;
       any_flags = 1;
@@ -272,10 +279,16 @@
           pi2->min_digits = 0;
           pi2->max_digits = 8;
           pi2->max_tries = 1;
-          rc = agent_askpin (ctrl, _("Repeat this PIN"), prompt, NULL, pi2);
+          rc = agent_askpin (ctrl,
+                             (resetcode?
+                              _("Repeat this Reset Code"):
+                              _("Repeat this PIN")),
+                             prompt, NULL, pi2);
           if (!rc && strcmp (pi->pin, pi2->pin))
             {
-              again_text = N_("PIN not correctly repeated; try again");
+              again_text = (resetcode? 
+                            N_("Reset Code not correctly repeated; try again"):
+                            N_("PIN not correctly repeated; try again"));
               xfree (pi2);
               xfree (pi);
               goto again;

Modified: trunk/g10/call-agent.c
===================================================================
--- trunk/g10/call-agent.c	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/call-agent.c	2008-09-25 10:06:02 UTC (rev 4835)
@@ -264,6 +264,8 @@
     {
       xfree (parm->serialno);
       parm->serialno = store_serialno (line);
+      parm->is_v2 = (strlen (parm->serialno) >= 16 
+                     && xtoi_2 (parm->serialno+12) >= 2 );
     }
   else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
     {
@@ -758,10 +760,12 @@
 
 /* Change the PIN of an OpenPGP card or reset the retry counter.
    CHVNO 1: Change the PIN
-         2: Same as 1
+         2: For v1 cards: Same as 1.
+            For v2 cards: Reset the PIN using the Reset Code.
          3: Change the admin PIN
        101: Set a new PIN and reset the retry counter
-       102: Same as 101
+       102: For v1 cars: Same as 101.
+            For v2 cards: Set a new Reset Code.
    SERIALNO is not used.
  */
 int

Modified: trunk/g10/call-agent.h
===================================================================
--- trunk/g10/call-agent.h	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/call-agent.h	2008-09-25 10:06:02 UTC (rev 4835)
@@ -49,6 +49,7 @@
   int chv1_cached;   /* True if a PIN is not required for each
                         signing.  Note that the gpg-agent might cache
                         it anyway. */
+  int is_v2;         /* True if this is a v2 card.  */
   int chvmaxlen[3];  /* Maximum allowed length of a CHV. */
   int chvretry[3];   /* Allowed retries for the CHV; 0 = blocked. */
 };

Modified: trunk/g10/card-util.c
===================================================================
--- trunk/g10/card-util.c	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/g10/card-util.c	2008-09-25 10:06:02 UTC (rev 4835)
@@ -51,7 +51,7 @@
 /* Change the PIN of a an OpenPGP card.  This is an interactive
    function. */
 void
-change_pin (int chvno, int allow_admin)
+change_pin (int unblock_v2, int allow_admin)
 {
   struct agent_card_info_s info;
   int rc;
@@ -76,8 +76,27 @@
       return;
     }
 
-  if(!allow_admin)
+
+  if (unblock_v2)
     {
+      if (!info.is_v2)
+        log_error (_("This command is only available for version 2 cards\n"));
+      else if (!info.chvretry[1])
+        log_error (_("Reset Code not or not anymore available\n"));
+      else
+        {
+          rc = agent_scd_change_pin (2, info.serialno);
+          if (rc)
+            tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+          else
+            {
+              write_status (STATUS_SC_OP_SUCCESS);
+              tty_printf ("PIN changed.\n");
+            }
+        }
+    }
+  else if (!allow_admin)
+    {
       rc = agent_scd_change_pin (1, info.serialno);
       if (rc)
 	tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -96,6 +115,7 @@
 	tty_printf ("1 - change PIN\n"
 		    "2 - unblock PIN\n"
 		    "3 - change Admin PIN\n"
+                    "4 - set the Reset Code\n"
 		    "Q - quit\n");
 	tty_printf ("\n");
 
@@ -107,6 +127,7 @@
 	rc = 0;
 	if (*answer == '1')
 	  {
+            /* Change PIN.  */
 	    rc = agent_scd_change_pin (1, info.serialno);
 	    if (rc)
 	      tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -118,6 +139,7 @@
 	  }
 	else if (*answer == '2')
 	  {
+            /* Unblock PIN.  */
 	    rc = agent_scd_change_pin (101, info.serialno);
 	    if (rc)
 	      tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
@@ -129,6 +151,7 @@
           }
 	else if (*answer == '3')
 	  {
+            /* Change Admin PIN.  */
 	    rc = agent_scd_change_pin (3, info.serialno);
 	    if (rc)
 	      tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
@@ -138,6 +161,19 @@
                 tty_printf ("PIN changed.\n");
               }
 	  }
+	else if (*answer == '4')
+	  {
+            /* Set a new Reset Code.  */
+	    rc = agent_scd_change_pin (102, info.serialno);
+	    if (rc)
+	      tty_printf ("Error setting the Reset Code: %s\n", 
+                          gpg_strerror (rc));
+	    else
+              {
+                write_status (STATUS_SC_OP_SUCCESS);
+                tty_printf ("Reset Code set.\n");
+              }
+	  }
 	else if (*answer == 'q' || *answer == 'Q')
 	  {
 	    break;
@@ -1345,6 +1381,7 @@
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
+    cmdUNBLOCK,
     cmdINVCMD
   };
 
@@ -1375,6 +1412,7 @@
     { "generate", cmdGENERATE, 1, N_("generate new keys")},
     { "passwd"  , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
+    { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "writecert", cmdWRITECERT, 1, NULL },
@@ -1644,6 +1682,11 @@
           did_checkpin = 0; /* Need to reset it of course. */
           break;
 
+        case cmdUNBLOCK:
+          change_pin (1, allow_admin);
+          did_checkpin = 0; /* Need to reset it of course. */
+          break;
+
         case cmdQUIT:
           goto leave;
 

Modified: trunk/scd/app-openpgp.c
===================================================================
--- trunk/scd/app-openpgp.c	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/app-openpgp.c	2008-09-25 10:06:02 UTC (rev 4835)
@@ -19,6 +19,29 @@
  * $Id$
  */
 
+/* Some notes:
+
+   CHV means Card Holder Verification and is nothing else than a PIN
+   or password.  That term seems to have been used originally with GSM
+   cards.  Version v2 of the specs changes the term to the clearer
+   term PW for password.  We use the terms here interchangeable
+   because we do not want to change existing strings i18n wise.
+
+   Version 2 of the specs also drops the separate PW2 which was
+   required in v1 due to ISO requirements.  It is now possible to have
+   one physical PW but two reference to it so that they can be
+   individually be verified (e.g. to implement a forced verification
+   for one key).  Thus you will noticed the use of PW2 with the verify
+   command but not with change_reference_data because the latter
+   operates directly on the physical PW.
+
+   The Reset Code (RC) as implemented by v2 cards uses the same error
+   counter as the PW2 of v1 cards.  By default no RC is set and thus
+   that error counter is set to 0.  After setting the RC the error
+   counter will be initialized to 3.
+
+ */
+
 #include <config.h>
 #include <errno.h>
 #include <stdio.h>
@@ -46,6 +69,7 @@
 #include "tlv.h"
 
 
+/* A table describing the DOs of the card.  */
 static struct {
   int tag;
   int constructed;
@@ -91,6 +115,18 @@
 };
 
 
+/* The format of RSA private keys.  */
+typedef enum
+  { 
+    RSA_UNKNOWN_FMT,
+    RSA_STD,
+    RSA_STD_N,
+    RSA_CRT,
+    RSA_CRT_N
+  } 
+rsa_key_format_t;
+
+
 /* One cache item for DOs.  */
 struct cache_s {
   struct cache_s *next;
@@ -150,6 +186,16 @@
     unsigned int no_sync:1;   /* Do not sync CHV1 and CHV2 */
     unsigned int def_chv2:1;  /* Use 123456 for CHV2.  */
   } flags;
+
+  struct
+  {
+    unsigned int n_bits;     /* Size of the modulus in bits.  The rest
+                                of this strucuire is only valid if
+                                this is not 0.  */
+    unsigned int e_bits;     /* Size of the public exponent in bits.  */
+    rsa_key_format_t format;  
+  } keyattr[3];
+
 };
 
 
@@ -1495,8 +1541,7 @@
 
   app->did_chv2 = 1;
   
-  if (!app->did_chv1 && !app->force_chv1 && pinvalue 
-      && !app->app_local->extcap.is_v2)
+  if (!app->did_chv1 && !app->force_chv1 && pinvalue)
     {
       /* For convenience we verify CHV1 here too.  We do this only if
          the card is not configured to require a verification before
@@ -1635,6 +1680,7 @@
           flush_cache_after_error (app);
           return rc;
         }
+      app->did_chv3 = 1;
     }
   return rc;
 }
@@ -1673,7 +1719,6 @@
     { "CERT-3",       0x7F21, 3, 0, 1 },
     { "SM-KEY-ENC",   0x00D1, 3, 0, 1 },
     { "SM-KEY-MAC",   0x00D2, 3, 0, 1 },
-    { "PW-RESET-CODE",0x00D3, 3, 0, 1 },
     { NULL, 0 }
   };
   int exmode;
@@ -1758,8 +1803,10 @@
 {
   int rc = 0;
   int chvno = atoi (chvnostr);
+  char *resetcode = NULL;
   char *pinvalue;
   int reset_mode = !!(flags & APP_CHANGE_FLAG_RESET);
+  int set_resetcode = 0;
 
   if (reset_mode && chvno == 3)
     {
@@ -1768,12 +1815,55 @@
     }
   else if (reset_mode || chvno == 3)
     {
-      /* we always require that the PIN is entered. */
+      /* We always require that the PIN is entered. */
       app->did_chv3 = 0;
       rc = verify_chv3 (app, pincb, pincb_arg);
       if (rc)
         goto leave;
+  
+      if (chvno == 2 && app->app_local->extcap.is_v2)
+        set_resetcode = 1;
     }
+  else if (chvno == 2 && app->app_local->extcap.is_v2)
+    {
+      /* There is no PW2 for v2 cards.  We use this condition to allow
+         a PW reset using the Reset Code.  */
+      void *relptr;
+      unsigned char *value;
+      size_t valuelen;
+      int remaining;
+
+      relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+      if (!relptr || valuelen < 7)
+        {
+          log_error (_("error retrieving CHV status from card\n"));
+          xfree (relptr);
+          rc = gpg_error (GPG_ERR_CARD);
+          goto leave;
+        }
+      remaining = value[5];
+      xfree (relptr);
+      if (!remaining)
+        {
+          log_error (_("Reset Code not or not anymore available\n"));
+          rc = gpg_error (GPG_ERR_BAD_PIN);
+          goto leave;
+        }          
+
+      rc = pincb (pincb_arg, _("||Please enter the Reset Code for the card"),
+                  &resetcode); 
+      if (rc)
+        {
+          log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+          goto leave;
+        }
+      if (strlen (resetcode) < 8)
+        {
+          log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+          rc = gpg_error (GPG_ERR_BAD_PIN);
+          goto leave;
+        }
+    }
   else if (chvno == 1 || chvno == 2)
     {
       /* CHV1 and CVH2 should always have the same value, thus we
@@ -1802,7 +1892,9 @@
   /* 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. */
-  rc = pincb (pincb_arg, chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"), 
+  rc = pincb (pincb_arg, 
+              set_resetcode? _("|RN|New Reset Code") :
+              chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"), 
               &pinvalue); 
   if (rc)
     {
@@ -1810,11 +1902,38 @@
       goto leave;
     }
 
-  if (reset_mode)
+  if (resetcode)
     {
+      char *buffer;
+
+      buffer = xtrymalloc (strlen (resetcode) + strlen (pinvalue) + 1);
+      if (!buffer)
+        rc = gpg_error_from_syserror ();
+      else
+        {
+          strcpy (stpcpy (buffer, resetcode), pinvalue);
+          rc = iso7816_reset_retry_counter_with_rc (app->slot, 0x81,
+                                                    buffer, strlen (buffer));
+          wipememory (buffer, strlen (buffer));
+          xfree (buffer);
+        }
+    }
+  else if (set_resetcode)
+    {
+      if (strlen (pinvalue) < 8)
+        {
+          log_error (_("Reset Code is too short; minimum length is %d\n"), 8);
+          rc = gpg_error (GPG_ERR_BAD_PIN);
+        }
+      else
+        rc = iso7816_put_data (app->slot, 0, 0xD3,
+                               pinvalue, strlen (pinvalue));
+    }
+  else if (reset_mode)
+    {
       rc = iso7816_reset_retry_counter (app->slot, 0x81,
                                         pinvalue, strlen (pinvalue));
-      if (!rc)
+      if (!rc && !app->app_local->extcap.is_v2)
         rc = iso7816_reset_retry_counter (app->slot, 0x82,
                                           pinvalue, strlen (pinvalue));
     }
@@ -1824,7 +1943,7 @@
         {
           rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
                                               pinvalue, strlen (pinvalue));
-          if (!rc)
+          if (!rc && !app->app_local->extcap.is_v2)
             rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
                                                 pinvalue, strlen (pinvalue));
         }
@@ -1832,11 +1951,20 @@
         rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
                                             pinvalue, strlen (pinvalue));
     }
-  xfree (pinvalue);
+  if (pinvalue)
+    {
+      wipememory (pinvalue, strlen (pinvalue));
+      xfree (pinvalue);
+    }
   if (rc)
     flush_cache_after_error (app);
 
  leave:
+  if (resetcode)
+    {
+      wipememory (resetcode, strlen (resetcode));
+      xfree (resetcode);
+    }
   return rc;
 }
 
@@ -1883,7 +2011,163 @@
 }
 
 
+/* Create a TLV tag and value and store it at BUFFER.  Return the length
+   of tag and length.  A LENGTH greater than 65535 is truncated. */
+static size_t
+add_tlv (unsigned char *buffer, unsigned int tag, size_t length)
+{ 
+  unsigned char *p = buffer;
 
+  assert (tag <= 0xffff);
+  if ( tag > 0xff )
+    *p++ = tag >> 8;
+  *p++ = tag;
+  if (length < 128)
+    *p++ = length;
+  else if (length < 256)
+    {
+      *p++ = 0x81;
+      *p++ = length;
+    }
+  else
+    {
+      if (length > 0xffff)
+        length = 0xffff;
+      *p++ = 0x82;
+      *p++ = length >> 8;
+      *p++ = length;
+    }
+
+  return p - buffer;
+}
+
+
+/* Build the private key template as specified in the OpenPGP specs
+   v2.0 section 4.3.3.7.  */
+static gpg_error_t
+build_privkey_template (app_t app, int keyno,
+                        const unsigned char *rsa_n, size_t rsa_n_len,
+                        const unsigned char *rsa_e, size_t rsa_e_len,
+                        const unsigned char *rsa_p, size_t rsa_p_len,
+                        const unsigned char *rsa_q, size_t rsa_q_len,
+                        unsigned char **result, size_t *resultlen)
+{
+  size_t rsa_e_reqlen;
+  unsigned char privkey[7*(1+3)];
+  size_t privkey_len;
+  unsigned char exthdr[2+2+3];
+  size_t exthdr_len;
+  unsigned char suffix[2+3];
+  size_t suffix_len;
+  unsigned char *tp;
+  size_t datalen;
+  unsigned char *template;
+  size_t template_size;
+
+  *result = NULL;
+  *resultlen = 0;
+
+  switch (app->app_local->keyattr[keyno].format)
+    {
+    case RSA_STD:
+    case RSA_STD_N:
+      break;
+    case RSA_CRT:
+    case RSA_CRT_N:
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+    default:
+      return gpg_error (GPG_ERR_INV_VALUE);
+    }
+
+  /* Get the required length for E.  */
+  rsa_e_reqlen = app->app_local->keyattr[keyno].e_bits/8;
+  assert (rsa_e_len <= rsa_e_reqlen);
+
+  /* Build the 7f48 cardholder private key template.  */
+  datalen = 0;
+  tp = privkey;
+
+  tp += add_tlv (tp, 0x91, rsa_e_reqlen);
+  datalen += rsa_e_reqlen;
+
+  tp += add_tlv (tp, 0x92, rsa_p_len);
+  datalen += rsa_p_len;
+
+  tp += add_tlv (tp, 0x93, rsa_q_len);
+  datalen += rsa_q_len;
+
+  if (app->app_local->keyattr[keyno].format == RSA_STD_N
+      || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+    {
+      tp += add_tlv (tp, 0x97, rsa_n_len);
+      datalen += rsa_n_len;
+    }
+  privkey_len = tp - privkey;
+
+  /* Build the extended header list without the private key template.  */
+  tp = exthdr;
+  *tp++ = keyno ==0 ? 0xb6 : keyno == 1? 0xb8 : 0xa4;
+  *tp++ = 0;
+  tp += add_tlv (tp, 0x7f48, privkey_len);
+  exthdr_len = tp - exthdr;
+
+  /* Build the 5f48 suffix of the data.  */
+  tp = suffix;
+  tp += add_tlv (tp, 0x5f48, datalen);
+  suffix_len = tp - suffix;
+
+  /* Now concatenate everything.  */
+  template_size = (1 + 3   /* 0x4d and len. */
+                   + exthdr_len
+                   + privkey_len
+                   + suffix_len
+                   + datalen);
+  tp = template = xtrymalloc_secure (template_size);
+  if (!template)
+    return gpg_error_from_syserror ();
+
+  tp += add_tlv (tp, 0x4d, exthdr_len + privkey_len + suffix_len + datalen);
+  memcpy (tp, exthdr, exthdr_len);
+  tp += exthdr_len;
+  memcpy (tp, privkey, privkey_len);
+  tp += privkey_len;
+  memcpy (tp, suffix, suffix_len);
+  tp += suffix_len;
+
+  memcpy (tp, rsa_e, rsa_e_len);
+  if (rsa_e_len < rsa_e_reqlen)
+    {
+      /* Right justify E. */
+      memmove (tp + rsa_e_reqlen - rsa_e_len, tp, rsa_e_len);
+      memset (tp, 0, rsa_e_reqlen - rsa_e_len);
+    }                 
+  tp += rsa_e_reqlen;
+      
+  memcpy (tp, rsa_p, rsa_p_len);
+  tp += rsa_p_len;
+      
+  memcpy (tp, rsa_q, rsa_q_len);
+  tp += rsa_q_len;
+  
+  if (app->app_local->keyattr[keyno].format == RSA_STD_N
+      || app->app_local->keyattr[keyno].format == RSA_CRT_N)
+    {
+      memcpy (tp, rsa_n, rsa_n_len);
+      tp += rsa_n_len;
+    }
+
+  /* Sanity check.  We don't know the exact length because we
+     allocated 3 bytes for the first length header.  */
+  assert (tp - template <= template_size);
+
+  *result = template;
+  *resultlen = tp - template;
+  return 0;
+}
+
+
+
 /* Handle the WRITEKEY command for OpenPGP.  This function expects a
    canonical encoded S-expression with the secret key in KEYDATA and
    its length (for assertions) in KEYDATALEN.  KEYID needs to be the
@@ -1910,6 +2194,7 @@
   const unsigned char *rsa_q = NULL;
   size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
   unsigned int nbits;
+  unsigned int maxbits;
   unsigned char *template = NULL;
   unsigned char *tp;
   size_t template_len;
@@ -2049,92 +2334,133 @@
       err = gpg_error (GPG_ERR_INV_VALUE);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].n_bits;
   nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
-  if (nbits != 1024)
+  if (nbits != maxbits)
     {
-      log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+      log_error (_("RSA modulus missing or not of size %d bits\n"), 
+                 (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].e_bits;
+  if (maxbits > 32 && !app->app_local->extcap.is_v2)
+    maxbits = 32; /* Our code for v1 does only support 32 bits.  */
   nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
-  if (nbits < 2 || nbits > 32)
+  if (nbits < 2 || nbits > maxbits)
     {
       log_error (_("RSA public exponent missing or larger than %d bits\n"),
-                 32);
+                 (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
+
+  maxbits = app->app_local->keyattr[keyno].n_bits/2;
   nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
-  if (nbits != 512)
+  if (nbits != maxbits)
     {
-      log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), 
+                 "P", (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
   nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
-  if (nbits != 512)
+  if (nbits != maxbits)
     {
-      log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+      log_error (_("RSA prime %s missing or not of size %d bits\n"), 
+                 "Q", (int)maxbits);
       err = gpg_error (GPG_ERR_BAD_SECKEY);
       goto leave;
     }
   
-
-  /* Build the private key template as described in section 4.3.3.6 of
-     the OpenPGP card specs:
-         0xC0   <length> public exponent
-         0xC1   <length> prime p 
-         0xC2   <length> prime q 
-  */
-  assert (rsa_e_len <= 4);
-  template_len = (1 + 1 + 4
-                  + 1 + 1 + rsa_p_len
-                  + 1 + 1 + rsa_q_len);
-  template = tp = xtrymalloc_secure (template_len);
-  if (!template)
-    {
-      err = gpg_error_from_syserror ();
-      goto leave;
-    }
-  *tp++ = 0xC0;
-  *tp++ = 4;
-  memcpy (tp, rsa_e, rsa_e_len);
-  if (rsa_e_len < 4)
-    {
-      /* Right justify E. */
-      memmove (tp+4-rsa_e_len, tp, rsa_e_len);
-      memset (tp, 0, 4-rsa_e_len);
-    }                 
-  tp += 4;
-
-  *tp++ = 0xC1;
-  *tp++ = rsa_p_len;
-  memcpy (tp, rsa_p, rsa_p_len);
-  tp += rsa_p_len;
-
-  *tp++ = 0xC2;
-  *tp++ = rsa_q_len;
-  memcpy (tp, rsa_q, rsa_q_len);
-  tp += rsa_q_len;
-
-  assert (tp - template == template_len);
-
-
-  /* Obviously we need to remove the cached public key.  */
+  /* We need to remove the cached public key.  */
   xfree (app->app_local->pk[keyno].key);
   app->app_local->pk[keyno].key = NULL;
   app->app_local->pk[keyno].keylen = 0;
   app->app_local->pk[keyno].read_done = 0;
 
-  /* Prepare for storing the key.  */
-  err = verify_chv3 (app, pincb, pincb_arg);
-  if (err)
-    goto leave;
 
-  /* Store the key. */
-  err = iso7816_put_data (app->slot, 0,
-                         (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
-                         template, template_len);
+  if (app->app_local->extcap.is_v2)
+    {
+      /* Build the private key template as described in section 4.3.3.7 of
+         the OpenPGP card specs version 2.0.  */
+      int exmode;
+ 
+      err = build_privkey_template (app, keyno,
+                                    rsa_n, rsa_n_len,
+                                    rsa_e, rsa_e_len,
+                                    rsa_p, rsa_p_len,
+                                    rsa_q, rsa_q_len,
+                                    &template, &template_len);
+      if (err)
+        goto leave;
+
+      /* Prepare for storing the key.  */
+      err = verify_chv3 (app, pincb, pincb_arg);
+      if (err)
+        goto leave;
+
+      /* Store the key. */
+      if (app->app_local->cardcap.cmd_chaining && template_len > 254)
+        exmode = -254;
+      else
+        exmode = 0;
+      err = iso7816_put_data_odd (app->slot, exmode, 0x3fff,
+                                  template, template_len);
+    }
+  else
+    {
+      /* Build the private key template as described in section 4.3.3.6 of
+         the OpenPGP card specs version 1.1:
+         0xC0   <length> public exponent
+         0xC1   <length> prime p 
+         0xC2   <length> prime q 
+      */
+      assert (rsa_e_len <= 4);
+      template_len = (1 + 1 + 4
+                      + 1 + 1 + rsa_p_len
+                      + 1 + 1 + rsa_q_len);
+      template = tp = xtrymalloc_secure (template_len);
+      if (!template)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+      *tp++ = 0xC0;
+      *tp++ = 4;
+      memcpy (tp, rsa_e, rsa_e_len);
+      if (rsa_e_len < 4)
+        {
+          /* Right justify E. */
+          memmove (tp+4-rsa_e_len, tp, rsa_e_len);
+          memset (tp, 0, 4-rsa_e_len);
+        }                 
+      tp += 4;
+      
+      *tp++ = 0xC1;
+      *tp++ = rsa_p_len;
+      memcpy (tp, rsa_p, rsa_p_len);
+      tp += rsa_p_len;
+      
+      *tp++ = 0xC2;
+      *tp++ = rsa_q_len;
+      memcpy (tp, rsa_q, rsa_q_len);
+      tp += rsa_q_len;
+      
+      assert (tp - template == template_len);
+      
+      /* Prepare for storing the key.  */
+      err = verify_chv3 (app, pincb, pincb_arg);
+      if (err)
+        goto leave;
+
+      /* Store the key. */
+      err = iso7816_put_data (app->slot, 0,
+                              (app->card_version > 0x0007? 0xE0:0xE9)+keyno,
+                              template, template_len);
+    }
   if (err)
     {
       log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
@@ -2891,8 +3217,8 @@
 }
 
 
-/* Read and parse the algorithm attributes for KEYNO.  KEYNO must be
-   in the range 0..2.  */
+/* Parse and optionally show the algorithm attributes for KEYNO.
+   KEYNO must be in the range 0..2.  */
 static void 
 parse_algorithm_attribute (app_t app, int keyno)
 { 
@@ -2903,6 +3229,8 @@
 
   assert (keyno >=0 && keyno <= 2);
 
+  app->app_local->keyattr[keyno].n_bits = 0;
+      
   relptr = get_one_do (app, 0xC1+keyno, &buffer, &buflen, NULL);
   if (!relptr)
     {
@@ -2916,21 +3244,33 @@
       return;
     }
 
-  log_info ("Key-Attr-%s ..: ", desc[keyno]);
+  if (opt.verbose)
+    log_info ("Key-Attr-%s ..: ", desc[keyno]);
   if (*buffer == 1 && (buflen == 5 || buflen == 6))
     {
-      log_printf ("RSA, n=%d, e=%d",
-                  (buffer[1]<<8 | buffer[2]),
-                  (buffer[3]<<8 | buffer[4]));
-      if (buflen == 6)
-        log_printf (", format=%s",
-                    buffer[5] == 0? "std" :
-                    buffer[5] == 1? "std+n" :
-                    buffer[5] == 2? "crt" :
-                    buffer[5] == 2? "crt+n" : "?");
-      log_printf ("\n");
+      app->app_local->keyattr[keyno].n_bits = (buffer[1]<<8 | buffer[2]);
+      app->app_local->keyattr[keyno].e_bits = (buffer[3]<<8 | buffer[4]);
+      app->app_local->keyattr[keyno].format = 0;
+      if (buflen < 6)
+        app->app_local->keyattr[keyno].format = RSA_STD;
+      else
+        app->app_local->keyattr[keyno].format = (buffer[5] == 0? RSA_STD   :
+                                                 buffer[5] == 1? RSA_STD_N :
+                                                 buffer[5] == 2? RSA_CRT   :
+                                                 buffer[5] == 3? RSA_CRT_N : 
+                                                 RSA_UNKNOWN_FMT);
+
+      if (opt.verbose)
+        log_printf
+          ("RSA, n=%u, e=%u, fmt=%s\n",
+           app->app_local->keyattr[keyno].n_bits,
+           app->app_local->keyattr[keyno].e_bits,
+           app->app_local->keyattr[keyno].format == RSA_STD?  "std"  :
+           app->app_local->keyattr[keyno].format == RSA_STD_N?"std+n":
+           app->app_local->keyattr[keyno].format == RSA_CRT?  "crt"  :
+           app->app_local->keyattr[keyno].format == RSA_CRT_N?"crt+n":"?");
     }
-  else
+  else if (opt.verbose)
     log_printhex ("", buffer, buflen);
 
   xfree (relptr);
@@ -3057,12 +3397,9 @@
       if (opt.verbose)
         show_caps (app->app_local);
 
-      if (opt.verbose)
-        {
-          parse_algorithm_attribute (app, 0);
-          parse_algorithm_attribute (app, 1);
-          parse_algorithm_attribute (app, 2);
-        }
+      parse_algorithm_attribute (app, 0);
+      parse_algorithm_attribute (app, 1);
+      parse_algorithm_attribute (app, 2);
       
       if (opt.verbose > 1)
         dump_all_do (slot);

Modified: trunk/scd/iso7816.c
===================================================================
--- trunk/scd/iso7816.c	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/iso7816.c	2008-09-25 10:06:02 UTC (rev 4835)
@@ -1,5 +1,5 @@
 /* iso7816.c - ISO 7816 commands
- *	Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2003, 2004, 2008 Free Software Foundation, Inc.
  *
  * This file is part of GnuPG.
  *
@@ -335,6 +335,7 @@
   if (!newchv || !newchvlen )
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  /* FIXME:  The keypad mode has not yet been tested.  */
   if (pininfo && pininfo->mode)
     sw = apdu_send_simple_kp (slot, 0x00, CMD_RESET_RETRY_COUNTER,
                            2, chvno, newchvlen, newchv,
@@ -350,6 +351,21 @@
 
 
 gpg_error_t
+iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+                                     const char *data, size_t datalen)
+{
+  int sw;
+
+  if (!data || !datalen )
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  sw = apdu_send_simple (slot, 0, 0x00, CMD_RESET_RETRY_COUNTER,
+                         0, chvno, datalen, data);
+  return map_sw (sw);
+}
+
+
+gpg_error_t
 iso7816_reset_retry_counter (int slot, int chvno,
                              const char *newchv, size_t newchvlen)
 {
@@ -404,6 +420,19 @@
   return map_sw (sw);
 }
 
+/* Same as iso7816_put_data but uses an odd instrcution byte.  */
+gpg_error_t
+iso7816_put_data_odd (int slot, int extended_mode, int tag,
+                      const unsigned char *data, size_t datalen)
+{
+  int sw;
+
+  sw = apdu_send_simple (slot, extended_mode, 0x00, CMD_PUT_DATA+1,
+                         ((tag >> 8) & 0xff), (tag & 0xff),
+                         datalen, (const char*)data);
+  return map_sw (sw);
+}
+
 /* Manage Security Environment.  This is a weird operation and there
    is no easy abstraction for it.  Furthermore, some card seem to have
    a different interpreation of 7816-8 and thus we resort to let the

Modified: trunk/scd/iso7816.h
===================================================================
--- trunk/scd/iso7816.h	2008-09-25 04:00:36 UTC (rev 4834)
+++ trunk/scd/iso7816.h	2008-09-25 10:06:02 UTC (rev 4835)
@@ -77,10 +77,15 @@
                                             const char *newchv,
                                             size_t newchvlen,
                                             iso7816_pininfo_t *pininfo);
+gpg_error_t iso7816_reset_retry_counter_with_rc (int slot, int chvno,
+                                                 const char *data,
+                                                 size_t datalen);
 gpg_error_t iso7816_get_data (int slot, int tag,
                               unsigned char **result, size_t *resultlen);
 gpg_error_t iso7816_put_data (int slot, int extended_mode, int tag,
                               const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_put_data_odd (int slot, int extended_mode, int tag,
+                                  const unsigned char *data, size_t datalen);
 gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
                                          const unsigned char *data,
                                          size_t datalen);




More information about the Gnupg-commits mailing list