g10: Add "key-attr" command for --card-edit.
[gnupg.git] / g10 / card-util.c
index 62b2a67..055c9fb 100644 (file)
@@ -212,9 +212,11 @@ get_manufacturer (unsigned int no)
     case 0x000A: return "Dangerous Things";
 
     case 0x002A: return "Magrathea";
+    case 0x0042: return "GnuPG e.V.";
 
     case 0x1337: return "Warsaw Hackerspace";
     case 0x2342: return "warpzone"; /* hackerspace Muenster.  */
+    case 0xBD0E: return "Paranoidlabs";
     case 0xF517: return "FSIJ";
 
       /* 0x0000 and 0xFFFF are defined as test cards per spec,
@@ -262,6 +264,21 @@ print_sha1_fpr_colon (estream_t fp, const unsigned char *fpr)
 
 
 static void
+print_keygrip (estream_t fp, const unsigned char *grp)
+{
+  int i;
+
+  if (opt.with_keygrip)
+    {
+      tty_fprintf (fp, "      keygrip ....: ");
+      for (i=0; i < 20 ; i++, grp++)
+        es_fprintf (fp, "%02X", *grp);
+      tty_fprintf (fp, "\n");
+    }
+}
+
+
+static void
 print_name (estream_t fp, const char *text, const char *name)
 {
   tty_fprintf (fp, "%s", text);
@@ -515,6 +532,11 @@ current_card_status (ctrl_t ctrl, estream_t fp,
       es_fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
                (unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
                (unsigned long)info.fpr3time);
+      es_fputs ("grp:", fp);
+      print_sha1_fpr_colon (fp, info.grp1);
+      print_sha1_fpr_colon (fp, info.grp2);
+      print_sha1_fpr_colon (fp, info.grp3);
+      es_putc ('\n', fp);
     }
   else
     {
@@ -591,18 +613,27 @@ current_card_status (ctrl_t ctrl, estream_t fp,
       tty_fprintf (fp, "Signature key ....:");
       print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
       if (info.fpr1valid && info.fpr1time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr1time));
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr1time));
+          print_keygrip (fp, info.grp1);
+        }
       tty_fprintf (fp, "Encryption key....:");
       print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
       if (info.fpr2valid && info.fpr2time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr2time));
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr2time));
+          print_keygrip (fp, info.grp2);
+        }
       tty_fprintf (fp, "Authentication key:");
       print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
       if (info.fpr3valid && info.fpr3time)
-        tty_fprintf (fp, "      created ....: %s\n",
-                     isotimestamp (info.fpr3time));
+        {
+          tty_fprintf (fp, "      created ....: %s\n",
+                       isotimestamp (info.fpr3time));
+          print_keygrip (fp, info.grp2);
+        }
       tty_fprintf (fp, "General key info..: ");
 
       thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
@@ -628,7 +659,7 @@ current_card_status (ctrl_t ctrl, estream_t fp,
 
 /* Print all available information for specific card with SERIALNO.
    Print all available information for current card when SERIALNO is NULL.
-   Or print llfor all cards when SERIALNO is "all".  */
+   Or print for all cards when SERIALNO is "all".  */
 void
 card_status (ctrl_t ctrl, estream_t fp, const char *serialno)
 {
@@ -1324,11 +1355,10 @@ show_keysize_warning (void)
 
 
 /* Ask for the size of a card key.  NBITS is the current size
-   configured for the card.  KEYNO is the number of the key used to
-   select the prompt.  Returns 0 to use the default size (i.e. NBITS)
-   or the selected size.  */
+   configured for the card.  Returns 0 to use the default size
+   (i.e. NBITS) or the selected size.  */
 static unsigned int
-ask_card_rsa_keysize (int keyno, unsigned int nbits)
+ask_card_rsa_keysize (unsigned int nbits)
 {
   unsigned int min_nbits = 1024;
   unsigned int max_nbits = 4096;
@@ -1337,13 +1367,7 @@ ask_card_rsa_keysize (int keyno, unsigned int nbits)
 
   for (;;)
     {
-      prompt = xasprintf
-        (keyno == 0?
-         _("What keysize do you want for the Signature key? (%u) "):
-         keyno == 1?
-         _("What keysize do you want for the Encryption key? (%u) "):
-         _("What keysize do you want for the Authentication key? (%u) "),
-         nbits);
+      prompt = xasprintf (_("What keysize do you want? (%u) "), nbits);
       answer = cpr_get ("cardedit.genkeys.size", prompt);
       cpr_kill_prompt ();
       req_nbits = *answer? atoi (answer): nbits;
@@ -1366,39 +1390,207 @@ ask_card_rsa_keysize (int keyno, unsigned int nbits)
         }
       else
         {
-          tty_printf (_("The card will now be re-configured "
-                        "to generate a key of %u bits\n"), req_nbits);
+          tty_printf (_("The card will now be re-configured"
+                        " to generate a key of %u bits\n"), req_nbits);
           show_keysize_warning ();
           return req_nbits;
         }
     }
 }
 
+/* Ask for the key attribute of a card key.  CURRENT is the current
+   attribute configured for the card.  KEYNO is the number of the key
+   used to select the prompt.  Returns NULL to use the default
+   attribute or the selected attribute structure.  */
+static struct key_attr *
+ask_card_keyattr (int keyno, const struct key_attr *current)
+{
+  struct key_attr *key_attr = NULL;
+  char *answer = NULL;
+  int algo;
 
-/* Change the size of key KEYNO (0..2) to NBITS and show an error
-   message if that fails.  */
+  tty_printf (_("Changing card key attribute for: "));
+  if (keyno == 0)
+    tty_printf (_("Signature key\n"));
+  else if (keyno == 1)
+    tty_printf (_("Encryption key\n"));
+  else
+    tty_printf (_("Authentication key\n"));
+
+  tty_printf (_("Please select what kind of key you want:\n"));
+  tty_printf (_("   (%d) RSA\n"), 1 );
+  tty_printf (_("   (%d) ECC\n"), 2 );
+
+  for (;;)
+    {
+      xfree (answer);
+      answer = cpr_get ("cardedit.genkeys.algo", _("Your selection? "));
+      cpr_kill_prompt ();
+      algo = *answer? atoi (answer) : 0;
+
+      if (!*answer || algo == 1 || algo == 2)
+        break;
+      else
+        tty_printf (_("Invalid selection.\n"));
+    }
+
+  if (algo == 0)
+    goto leave;
+
+  key_attr = xmalloc (sizeof (struct key_attr));
+
+  if (algo == 1)
+    {
+      unsigned int nbits, result_nbits;
+
+      if (current->algo == PUBKEY_ALGO_RSA)
+        nbits = current->nbits;
+      else
+        nbits = 2048;
+
+      result_nbits = ask_card_rsa_keysize (nbits);
+      if (result_nbits == 0)
+        {
+          if (current->algo == PUBKEY_ALGO_RSA)
+            {
+              xfree (key_attr);
+              key_attr = NULL;
+            }
+          else
+            result_nbits = nbits;
+        }
+
+      if (key_attr)
+        {
+          key_attr->algo = PUBKEY_ALGO_RSA;
+          key_attr->nbits = result_nbits;
+        }
+    }
+  else
+    {
+      const char *curve;
+      const char *oid_str;
+
+      if (current->algo == PUBKEY_ALGO_RSA)
+        {
+          if (keyno == 1)
+            /* Encryption key */
+            algo = PUBKEY_ALGO_ECDH;
+          else /* Signature key or Authentication key */
+            algo = PUBKEY_ALGO_ECDSA;
+          curve = NULL;
+        }
+      else
+        {
+          algo = current->algo;
+          curve = current->curve;
+        }
+
+      curve = ask_curve (&algo, NULL, curve);
+      if (curve)
+        {
+          key_attr->algo = algo;
+          oid_str = openpgp_curve_to_oid (curve, NULL);
+          key_attr->curve = openpgp_oid_to_curve (oid_str, 0);
+        }
+      else
+        {
+          xfree (key_attr);
+          key_attr = NULL;
+        }
+    }
+
+ leave:
+  if (!key_attr)
+    tty_printf (_("No change."));
+
+  return key_attr;
+}
+
+
+
+/* Change the key attribute of key KEYNO (0..2) and show an error
+ * message if that fails.  */
 static gpg_error_t
-do_change_rsa_keysize (int keyno, unsigned int nbits)
+do_change_keyattr (int keyno, const struct key_attr *key_attr)
 {
-  gpg_error_t err;
+  gpg_error_t err = 0;
   char args[100];
 
-  snprintf (args, sizeof args, "--force %d 1 rsa%u", keyno+1, nbits);
+  if (key_attr->algo == PUBKEY_ALGO_RSA)
+    snprintf (args, sizeof args, "--force %d 1 rsa%u", keyno+1,
+              key_attr->nbits);
+  else if (key_attr->algo == PUBKEY_ALGO_ECDH
+           || key_attr->algo == PUBKEY_ALGO_ECDSA
+           || key_attr->algo == PUBKEY_ALGO_EDDSA)
+    snprintf (args, sizeof args, "--force %d %d %s",
+              keyno+1, key_attr->algo, key_attr->curve);
+  else
+    {
+      log_error (_("public key algorithm %d (%s) is not supported\n"),
+                 key_attr->algo, gcry_pk_algo_name (key_attr->algo));
+      return gpg_error (GPG_ERR_PUBKEY_ALGO);
+    }
+
   err = agent_scd_setattr ("KEY-ATTR", args, strlen (args), NULL);
   if (err)
-    log_error (_("error changing size of key %d to %u bits: %s\n"),
-               keyno+1, nbits, gpg_strerror (err));
+    log_error (_("error changing key attribute for key %d: %s\n"),
+               keyno+1, gpg_strerror (err));
   return err;
 }
 
 
 static void
+key_attr (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  int keyno;
+
+  err = get_info_for_key_operation (&info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!(info.is_v2 && info.extcap.aac))
+    {
+      log_error (_("This command is not supported by this card\n"));
+      goto leave;
+    }
+
+  for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
+    {
+      struct key_attr *key_attr;
+
+      if ((key_attr = ask_card_keyattr (keyno, &info.key_attr[keyno])))
+        {
+          err = do_change_keyattr (keyno, key_attr);
+          xfree (key_attr);
+          if (err)
+            {
+              /* Error: Better read the default key attribute again.  */
+              agent_release_card_info (&info);
+              if (get_info_for_key_operation (&info))
+                goto leave;
+              /* Ask again for this key. */
+              keyno--;
+            }
+        }
+    }
+
+ leave:
+  agent_release_card_info (&info);
+}
+
+
+static void
 generate_card_keys (ctrl_t ctrl)
 {
   struct agent_card_info_s info;
   int forced_chv1;
   int want_backup;
-  int keyno;
 
   if (get_info_for_key_operation (&info))
     return;
@@ -1446,35 +1638,10 @@ generate_card_keys (ctrl_t ctrl)
       tty_printf ("\n");
     }
 
+
   if (check_pin_for_key_operation (&info, &forced_chv1))
     goto leave;
 
-  /* If the cards features changeable key attributes, we ask for the
-     key size.  */
-  if (info.is_v2 && info.extcap.aac)
-    {
-      unsigned int nbits;
-
-      for (keyno = 0; keyno < DIM (info.key_attr); keyno++)
-        {
-          if (info.key_attr[keyno].algo == PUBKEY_ALGO_RSA)
-            {
-              nbits = ask_card_rsa_keysize (keyno, info.key_attr[keyno].nbits);
-              if (nbits && do_change_rsa_keysize (keyno, nbits))
-                {
-                  /* Error: Better read the default key size again.  */
-                  agent_release_card_info (&info);
-                  if (get_info_for_key_operation (&info))
-                    goto leave;
-                  /* Ask again for this key size. */
-                  keyno--;
-                }
-            }
-        }
-      /* Note that INFO has not be synced.  However we will only use
-         the serialnumber and thus it won't harm.  */
-    }
-
   generate_keypair (ctrl, 1, NULL, info.serialno, want_backup);
 
  leave:
@@ -1533,30 +1700,6 @@ card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock)
   if (err)
     goto leave;
 
-  /* If the cards features changeable key attributes, we ask for the
-     key size.  */
-  if (info.is_v2 && info.extcap.aac)
-    {
-      if (info.key_attr[keyno-1].algo == PUBKEY_ALGO_RSA)
-        {
-          unsigned int nbits;
-
-        ask_again:
-          nbits = ask_card_rsa_keysize (keyno-1, info.key_attr[keyno-1].nbits);
-          if (nbits && do_change_rsa_keysize (keyno-1, nbits))
-            {
-              /* Error: Better read the default key size again.  */
-              agent_release_card_info (&info);
-              err = get_info_for_key_operation (&info);
-              if (err)
-                goto leave;
-              goto ask_again;
-            }
-        }
-      /* Note that INFO has not be synced.  However we will only use
-         the serialnumber and thus it won't harm.  */
-    }
-
   err = generate_card_subkeypair (ctrl, pub_keyblock, keyno, info.serialno);
 
  leave:
@@ -1727,10 +1870,8 @@ factory_reset (void)
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 20 00 83 08 40 40 40 40 40 40 40 40
         scd apdu 00 e6 00 00
-        scd reset
-        scd serialno undefined
-        scd apdu 00 A4 04 00 06 D2 76 00 01 24 01
         scd apdu 00 44 00 00
+        scd reset
         /echo Card has been reset to factory defaults
 
       but tries to find out something about the card first.
@@ -1743,7 +1884,7 @@ factory_reset (void)
   else if (err)
     {
       log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
-      return;
+      goto leave;
     }
 
   if (!termstate)
@@ -1793,10 +1934,16 @@ factory_reset (void)
          command because there is no machinery in scdaemon to catch
          the verify command and ask for the PIN when the "APDU"
          command is used. */
+      /* Here, the length of dummy wrong PIN is 32-byte, also
+         supporting authentication with KDF DO.  */
       for (i=0; i < 4; i++)
-        send_apdu ("00200081084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008120"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
       for (i=0; i < 4; i++)
-        send_apdu ("00200083084040404040404040", "VERIFY", 0xffff);
+        send_apdu ("0020008320"
+                   "40404040404040404040404040404040"
+                   "40404040404040404040404040404040", "VERIFY", 0xffff);
 
       /* Send terminate datafile command.  */
       err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
@@ -1804,17 +1951,6 @@ factory_reset (void)
         goto leave;
     }
 
-  /* The card is in termination state - reset and select again.  */
-  err = send_apdu (NULL, "RESET", 0);
-  if (err)
-    goto leave;
-  err = send_apdu ("undefined", "dummy select", 0);
-  if (err)
-    goto leave;
-
-  /* Select the OpenPGP application. (no error checking here). */
-  send_apdu ("00A4040006D27600012401", "SELECT AID", 0xffff);
-
   /* Send activate datafile command.  This is used without
      confirmation if the card is already in termination state.  */
   err = send_apdu ("00440000", "ACTIVATE DF", 0);
@@ -1823,8 +1959,16 @@ factory_reset (void)
 
   /* Finally we reset the card reader once more.  */
   err = send_apdu (NULL, "RESET", 0);
-  if (err)
-    goto leave;
+
+  /* Then, connect the card again.  */
+  if (!err)
+    {
+      char *serialno0;
+
+      err = agent_scd_serialno (&serialno0, NULL);
+      if (!err)
+        xfree (serialno0);
+    }
 
  leave:
   xfree (answer);
@@ -1832,6 +1976,104 @@ factory_reset (void)
 }
 
 
+#define USER_PIN_DEFAULT "123456"
+#define ADMIN_PIN_DEFAULT "12345678"
+#define KDF_DATA_LENGTH 110
+
+/* Generate KDF data.  */
+static gpg_error_t
+gen_kdf_data (unsigned char *data)
+{
+  const unsigned char h0[] = { 0x81, 0x01, 0x03,
+                               0x82, 0x01, 0x08,
+                               0x83, 0x04 };
+  const unsigned char h1[] = { 0x84, 0x08 };
+  const unsigned char h2[] = { 0x85, 0x08 };
+  const unsigned char h3[] = { 0x86, 0x08 };
+  const unsigned char h4[] = { 0x87, 0x20 };
+  const unsigned char h5[] = { 0x88, 0x20 };
+  unsigned char *p, *salt_user, *salt_admin;
+  unsigned char s2k_char;
+  unsigned int iterations;
+  unsigned char count_4byte[4];
+  gpg_error_t err = 0;
+
+  p = data;
+
+  s2k_char = encode_s2k_iterations (0);
+  iterations = S2K_DECODE_COUNT (s2k_char);
+  count_4byte[0] = (iterations >> 24) & 0xff;
+  count_4byte[1] = (iterations >> 16) & 0xff;
+  count_4byte[2] = (iterations >>  8) & 0xff;
+  count_4byte[3] = (iterations & 0xff);
+
+  memcpy (p, h0, sizeof h0);
+  p += sizeof h0;
+  memcpy (p, count_4byte, sizeof count_4byte);
+  p += sizeof count_4byte;
+  memcpy (p, h1, sizeof h1);
+  salt_user = (p += sizeof h1);
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h2, sizeof h2);
+  p += sizeof h2;
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h3, sizeof h3);
+  salt_admin = (p += sizeof h3);
+  gcry_randomize (p, 8, GCRY_STRONG_RANDOM);
+  p += 8;
+  memcpy (p, h4, sizeof h4);
+  p += sizeof h4;
+  err = gcry_kdf_derive (USER_PIN_DEFAULT, strlen (USER_PIN_DEFAULT),
+                         GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                         salt_user, 8, iterations, 32, p);
+  p += 32;
+  if (!err)
+    {
+      memcpy (p, h5, sizeof h5);
+      p += sizeof h5;
+      err = gcry_kdf_derive (ADMIN_PIN_DEFAULT, strlen (ADMIN_PIN_DEFAULT),
+                             GCRY_KDF_ITERSALTED_S2K, DIGEST_ALGO_SHA256,
+                             salt_admin, 8, iterations, 32, p);
+    }
+
+  return err;
+}
+
+/* Setup KDF data object which is used for PIN authentication.  */
+static void
+kdf_setup (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  unsigned char kdf_data[KDF_DATA_LENGTH];
+
+  memset (&info, 0, sizeof info);
+
+  err = agent_scd_getattr ("EXTCAP", &info);
+  if (err)
+    {
+      log_error (_("error getting card info: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!info.extcap.kdf)
+    {
+      log_error (_("This command is not supported by this card\n"));
+      goto leave;
+    }
+
+  if (!(err = gen_kdf_data (kdf_data))
+      && !(err = agent_scd_setattr ("KDF", kdf_data, KDF_DATA_LENGTH, NULL)))
+    err = agent_scd_getattr ("KDF", &info);
+
+  if (err)
+    log_error (_("error for setup KDF: %s\n"), gpg_strerror (err));
+
+ leave:
+  agent_release_card_info (&info);
+}
 \f
 /* Data used by the command parser.  This needs to be outside of the
    function scope to allow readline based command completion.  */
@@ -1841,7 +2083,8 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET,
+    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
+    cmdKEYATTR,
     cmdINVCMD
   };
 
@@ -1874,6 +2117,8 @@ static struct
     { "verify"  , cmdVERIFY, 0, N_("verify the PIN and list all data")},
     { "unblock" , cmdUNBLOCK,0, N_("unblock the PIN using a Reset Code") },
     { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
+    { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
+    { "key-attr", cmdKEYATTR, 1, N_("change the key attribute")},
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "readcert", cmdREADCERT, 0, NULL },
@@ -2157,6 +2402,14 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           factory_reset ();
           break;
 
+        case cmdKDFSETUP:
+          kdf_setup ();
+          break;
+
+        case cmdKEYATTR:
+          key_attr ();
+          break;
+
         case cmdQUIT:
           goto leave;