gpg: Add dedicated error code for PGP-2 keys.
[gnupg.git] / g10 / card-util.c
index abf234f..4b584bf 100644 (file)
@@ -1,5 +1,6 @@
 /* card-util.c - Utility functions for the OpenPGP card.
- * Copyright (C) 2003, 2004, 2005, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2005, 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2003-2005, 2009 Werner Koch
  *
  * This file is part of GnuPG.
  *
@@ -80,7 +81,7 @@ change_pin (int unblock_v2, int allow_admin)
   struct agent_card_info_s info;
   int rc;
 
-  rc = agent_learn (&info);
+  rc = agent_scd_learn (&info);
   if (rc)
     {
       log_error (_("OpenPGP card not available: %s\n"),
@@ -208,13 +209,16 @@ get_manufacturer (unsigned int no)
     case 0x0005: return "ZeitControl";
     case 0x0006: return "Yubico";
     case 0x0007: return "OpenKMS";
+    case 0x0008: return "LogoEmail";
 
     case 0x002A: return "Magrathea";
 
+    case 0x1337: return "Warsaw Hackerspace";
+
     case 0xF517: return "FSIJ";
 
-      /* 0x00000 and 0xFFFF are defined as test cards per spec,
-         0xFFF00 to 0xFFFE are assigned for use with randomly created
+      /* 0x0000 and 0xFFFF are defined as test cards per spec,
+         0xFF00 to 0xFFFE are assigned for use with randomly created
          serial numbers.  */
     case 0x0000:
     case 0xffff: return "test card";
@@ -370,7 +374,7 @@ card_status (estream_t fp, char *serialno, size_t serialnobuflen)
   if (serialno && serialnobuflen)
     *serialno = 0;
 
-  rc = agent_learn (&info);
+  rc = agent_scd_learn (&info);
   if (rc)
     {
       if (opt.with_colons)
@@ -750,7 +754,7 @@ fetch_url (ctrl_t ctrl)
                  gpg_strerror(rc));
       else if (info.pubkey_url && *info.pubkey_url)
        {
-         spec=parse_keyserver_uri(info.pubkey_url,1,NULL,0);
+         spec = parse_keyserver_uri (info.pubkey_url, 1);
          if(spec && info.fpr1valid)
            {
              /* This is not perfectly right.  Currently, all card
@@ -1282,7 +1286,7 @@ show_keysize_warning (void)
     return;
   shown = 1;
   tty_printf
-    (_("NOTE: There is no guarantee that the card "
+    (_("Note: There is no guarantee that the card "
        "supports the requested size.\n"
        "      If the key generation does not succeed, "
        "please check the\n"
@@ -1392,7 +1396,7 @@ generate_card_keys (ctrl_t ctrl)
        || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
     {
       tty_printf ("\n");
-      log_info (_("NOTE: keys are already stored on the card!\n"));
+      log_info (_("Note: keys are already stored on the card!\n"));
       tty_printf ("\n");
       if ( !cpr_get_answer_is_yes ("cardedit.genkeys.replace_keys",
                                    _("Replace existing keys? (y/N) ")))
@@ -1617,7 +1621,7 @@ card_store_subkey (KBNODE node, int use)
     goto leave;
 
   epoch2isotime (timebuf, (time_t)pk->timestamp);
-  agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf);
+  rc = agent_keytocard (hexgrip, keyno, rc, info.serialno, timebuf);
 
   if (rc)
     log_error (_("KEYTOCARD failed: %s\n"), gpg_strerror (rc));
@@ -1631,6 +1635,169 @@ card_store_subkey (KBNODE node, int use)
 }
 
 
+
+/* Direct sending of an hex encoded APDU with error printing.  */
+static gpg_error_t
+send_apdu (const char *hexapdu, const char *desc, unsigned int ignore)
+{
+  gpg_error_t err;
+  unsigned int sw;
+
+  err = agent_scd_apdu (hexapdu, &sw);
+  if (err)
+    tty_printf ("sending card command %s failed: %s\n", desc,
+                gpg_strerror (err));
+  else if (!hexapdu || !strcmp (hexapdu, "undefined"))
+    ;
+  else if (ignore == 0xffff)
+    ; /* Ignore all status words.  */
+  else if (sw != 0x9000)
+    {
+      switch (sw)
+        {
+        case 0x6285: err = gpg_error (GPG_ERR_OBJ_TERM_STATE); break;
+        case 0x6982: err = gpg_error (GPG_ERR_BAD_PIN); break;
+        case 0x6985: err = gpg_error (GPG_ERR_USE_CONDITIONS); break;
+        default: err = gpg_error (GPG_ERR_CARD);
+        }
+      if (!(ignore && ignore == sw))
+        tty_printf ("card command %s failed: %s (0x%04x)\n", desc,
+                    gpg_strerror (err),  sw);
+    }
+  return err;
+}
+
+
+/* Do a factory reset after confirmation.  */
+static void
+factory_reset (void)
+{
+  struct agent_card_info_s info;
+  gpg_error_t err;
+  char *answer = NULL;
+  int termstate = 0;
+  int i;
+
+  /*  The code below basically does the same what this
+      gpg-connect-agent script does:
+
+        scd reset
+        scd serialno undefined
+        scd apdu 00 A4 04 00 06 D2 76 00 01 24 01
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 08 40 40 40 40 40 40 40 40
+        scd apdu 00 20 00 81 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 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 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
+        /echo Card has been reset to factory defaults
+
+      but tries to find out something about the card first.
+   */
+
+  err = agent_scd_learn (&info);
+  if (gpg_err_code (err) == GPG_ERR_OBJ_TERM_STATE
+      && gpg_err_source (err) == GPG_ERR_SOURCE_SCD)
+    termstate = 1;
+  else if (err)
+    {
+      log_error (_("OpenPGP card not available: %s\n"), gpg_strerror (err));
+      return;
+    }
+
+  if (!termstate)
+    {
+      log_info (_("OpenPGP card no. %s detected\n"),
+                info.serialno? info.serialno : "[none]");
+      if (!(info.status_indicator == 3 || info.status_indicator == 5))
+        {
+          /* Note: We won't see status-indicator 3 here because it is not
+             possible to select a card application in termination state.  */
+          log_error (_("This command is not supported by this card\n"));
+          goto leave;
+        }
+
+      tty_printf ("\n");
+      log_info (_("Note: This command destroys all keys stored on the card!\n"));
+      tty_printf ("\n");
+      if (!cpr_get_answer_is_yes ("cardedit.factory-reset.proceed",
+                                  _("Continue? (y/N) ")))
+        goto leave;
+
+
+      answer = cpr_get ("cardedit.factory-reset.really",
+                        _("Really do a factory reset? (enter \"yes\") "));
+      cpr_kill_prompt ();
+      trim_spaces (answer);
+      if (strcmp (answer, "yes"))
+        goto leave;
+
+      /* We need to select a card application before we can send APDUs
+         to the card without scdaemon doing anything on its own.  */
+      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.  */
+      err = send_apdu ("00A4040006D27600012401", "SELECT AID", 0);
+      if (err)
+        goto leave;
+
+      /* Do some dummy verifies with wrong PINs to set the retry
+         counter to zero.  We can't easily use the card version 2.1
+         feature of presenting the admin PIN to allow the terminate
+         command because there is no machinery in scdaemon to catch
+         the verify command and ask for the PIN when the "APDU"
+         command is used. */
+      for (i=0; i < 4; i++)
+        send_apdu ("00200081084040404040404040", "VERIFY", 0xffff);
+      for (i=0; i < 4; i++)
+        send_apdu ("00200083084040404040404040", "VERIFY", 0xffff);
+
+      /* Send terminate datafile command.  */
+      err = send_apdu ("00e60000", "TERMINATE DF", 0x6985);
+      if (err)
+        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);
+  if (err)
+    goto leave;
+
+  /* Finally we reset the card reader once more.  */
+  err = send_apdu (NULL, "RESET", 0);
+  if (err)
+    goto leave;
+
+ leave:
+  xfree (answer);
+  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.  */
@@ -1640,7 +1807,7 @@ enum cmdids
     cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
-    cmdREADCERT, cmdUNBLOCK,
+    cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET,
     cmdINVCMD
   };
 
@@ -1672,6 +1839,7 @@ static struct
     { "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") },
+    { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
     /* Note, that we do not announce these command yet. */
     { "privatedo", cmdPRIVATEDO, 0, NULL },
     { "readcert", cmdREADCERT, 0, NULL },
@@ -1844,7 +2012,7 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           for (i=0; cmds[i].name; i++ )
             if(cmds[i].desc
               && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
-              tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+              tty_printf("%-14s %s\n", cmds[i].name, _(cmds[i].desc) );
           break;
 
        case cmdADMIN:
@@ -1949,6 +2117,10 @@ card_edit (ctrl_t ctrl, strlist_t commands)
           change_pin (1, allow_admin);
           break;
 
+        case cmdFACTORYRESET:
+          factory_reset ();
+          break;
+
         case cmdQUIT:
           goto leave;