[git] GnuPG - branch, master, updated. gnupg-2.2.7-350-gda38325

by Werner Koch cvs at cvs.gnupg.org
Thu Jan 31 16:08:06 CET 2019


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU Privacy Guard".

The branch, master has been updated
       via  da383257404cde9689bc58259ef3f46e9903bf34 (commit)
       via  1d57450f3e71b198e66e155a8ebbfab452f58ffc (commit)
      from  0107984f9f55f84e4842642bceefd2181ec09dab (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit da383257404cde9689bc58259ef3f46e9903bf34
Author: Werner Koch <wk at gnupg.org>
Date:   Thu Jan 31 16:06:47 2019 +0100

    card: New command 'authenticate'.
    
    * tools/card-tool-misc.c (hex_to_buffer): New.
    * tools/gpg-card-tool.c (get_data_from_file): Change to allow returning
    a string.
    (cmd_authenticate): New.
    (cmds): Add command "authenticate".
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/tools/card-tool-misc.c b/tools/card-tool-misc.c
index 0f5fcc0..06fcb67 100644
--- a/tools/card-tool-misc.c
+++ b/tools/card-tool-misc.c
@@ -42,3 +42,38 @@ find_kinfo (card_info_t info, const char *keyref)
       return kinfo;
   return NULL;
 }
+
+
+/* Convert STRING into a newly allocated buffer while translating the
+ * hex numbers.  Blanks and colons are allowed to separate pairs of
+ * hex digits.  Returns NULL on error or a newly malloced buffer and
+ * its length in LENGTH.  */
+void *
+hex_to_buffer (const char *string, size_t *r_length)
+{
+  unsigned char *buffer;
+  const char *s;
+  size_t n;
+
+  buffer = xtrymalloc (strlen (string)+1);
+  if (!buffer)
+    return NULL;
+  for (s=string, n=0; *s; s++)
+    {
+      if (ascii_isspace (*s) || *s == ':')
+        continue;
+      if (hexdigitp (s) && hexdigitp (s+1))
+        {
+          buffer[n++] = xtoi_2 (s);
+          s++;
+        }
+      else
+        {
+          xfree (buffer);
+          gpg_err_set_errno (EINVAL);
+          return NULL;
+        }
+    }
+  *r_length = n;
+  return buffer;
+}
diff --git a/tools/card-tool.h b/tools/card-tool.h
index 05d6ea4..bea618a 100644
--- a/tools/card-tool.h
+++ b/tools/card-tool.h
@@ -189,6 +189,7 @@ gpg_error_t test_get_matching_keys (const char *hexgrip);
 
 /*-- card-tool-misc.c --*/
 key_info_t find_kinfo (card_info_t info, const char *keyref);
+void *hex_to_buffer (const char *string, size_t *r_length);
 
 
 /*-- card-call-scd.c --*/
diff --git a/tools/gpg-card-tool.c b/tools/gpg-card-tool.c
index 07b8bc6..3145229 100644
--- a/tools/gpg-card-tool.c
+++ b/tools/gpg-card-tool.c
@@ -326,8 +326,11 @@ main (int argc, char **argv)
 
 /* Read data from file FNAME up to MAX_GET_DATA_FROM_FILE characters.
  * On error return an error code and stores NULL at R_BUFFER; on
- * success returns 0, stpres the number of bytes read at R_BUFLEN and
- * the address of a newly allocated buffer at R_BUFFER. */
+ * success returns 0 and stores the number of bytes read at R_BUFLEN
+ * and the address of a newly allocated buffer at R_BUFFER.  A
+ * complementary nul byte is always appended to the data but not
+ * counted; this allows to pass NULL for R-BUFFER and consider the
+ * returned data as a string. */
 static gpg_error_t
 get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen)
 {
@@ -337,7 +340,8 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen)
   int n;
 
   *r_buffer = NULL;
-  *r_buflen = 0;
+  if (r_buflen)
+    *r_buflen = 0;
 
   fp = es_fopen (fname, "rb");
   if (!fp)
@@ -356,7 +360,7 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen)
       return err;
     }
 
-  n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE, fp);
+  n = es_fread (data, 1, MAX_GET_DATA_FROM_FILE - 1, fp);
   es_fclose (fp);
   if (n < 0)
     {
@@ -365,8 +369,11 @@ get_data_from_file (const char *fname, char **r_buffer, size_t *r_buflen)
       xfree (data);
       return err;
     }
+  data[n] = 0;
+
   *r_buffer = data;
-  *r_buflen = n;
+  if (r_buflen)
+    *r_buflen = n;
   return 0;
 }
 
@@ -951,6 +958,73 @@ cmd_verify (card_info_t info, char *argstr)
 }
 
 
+static gpg_error_t
+cmd_authenticate (card_info_t info, char *argstr)
+{
+  gpg_error_t err;
+  int opt_setkey;
+  int opt_raw;
+  char *string = NULL;
+  char *key = NULL;
+  size_t keylen;
+
+  if (!info)
+    return print_help
+      ("AUTHENTICATE [--setkey] [--raw] [< FILE]|KEY\n\n"
+       "Perform a mutual autentication either by reading the key\n"
+       "from FILE or by taking it from the command line.  Without\n"
+       "the option --raw the key is expected to be hex encoded.\n"
+       "To install a new administration key --setkey is used; this\n"
+       "requires a prior authentication with the old key.",
+       APP_TYPE_PIV, 0);
+
+  if (info->apptype != APP_TYPE_PIV)
+    {
+      log_info ("Note: This is a PIV only command.\n");
+      return gpg_error (GPG_ERR_NOT_SUPPORTED);
+    }
+
+  opt_setkey = has_leading_option (argstr, "--setkey");
+  opt_raw = has_leading_option (argstr, "--raw");
+  argstr = skip_options (argstr);
+
+  if (*argstr == '<')  /* Read key from a file. */
+    {
+      for (argstr++; spacep (argstr); argstr++)
+        ;
+      err = get_data_from_file (argstr, &string, NULL);
+      if (err)
+        goto leave;
+    }
+
+  if (opt_raw)
+    {
+      key = string? string : xstrdup (argstr);
+      string = NULL;
+      keylen = strlen (key);
+    }
+  else
+    {
+      key = hex_to_buffer (string? string: argstr, &keylen);
+      if (!key)
+        {
+          err = gpg_error_from_syserror ();
+          goto leave;
+        }
+    }
+  err = scd_setattr (opt_setkey? "SET-ADM-KEY":"AUTH-ADM-KEY", key, keylen);
+
+ leave:
+  if (key)
+    {
+      wipememory (key, keylen);
+      xfree (key);
+    }
+  xfree (string);
+  return err;
+}
+
+
 /* Helper for cmd_name to qyery a part of name.  */
 static char *
 ask_one_name (const char *prompt)
@@ -2610,7 +2684,7 @@ enum cmdids
     cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSALUT, cmdCAFPR,
     cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO, cmdWRITECERT,
     cmdREADCERT, cmdUNBLOCK, cmdFACTORYRESET, cmdKDFSETUP,
-    cmdKEYATTR, cmdUIF,
+    cmdKEYATTR, cmdUIF, cmdAUTHENTICATE,
     cmdINVCMD
   };
 
@@ -2641,6 +2715,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")},
+  { "authenticate",cmdAUTHENTICATE, 0,N_("authenticate to the card")},
   { "reset"   , cmdRESET,  0, N_("send a reset to the card daemon")},
   { "factory-reset", cmdFACTORYRESET, 1, N_("destroy all keys and data")},
   { "kdf-setup", cmdKDFSETUP, 1, N_("setup KDF for PIN authentication")},
@@ -2871,6 +2946,7 @@ interactive_loop (void)
           if (!err)
             redisplay = 1;
           break;
+        case cmdAUTHENTICATE: err = cmd_authenticate (info, argstr); break;
         case cmdNAME:      err = cmd_name (info, argstr); break;
         case cmdURL:       err = cmd_url (info, argstr);  break;
 	case cmdFETCH:     err = cmd_fetch (info);  break;

commit 1d57450f3e71b198e66e155a8ebbfab452f58ffc
Author: Werner Koch <wk at gnupg.org>
Date:   Thu Jan 31 14:26:17 2019 +0100

    scd: Add DES authentication for PIV card.
    
    * scd/app-piv.c (flush_cached_data): New.
    (auth_adm_key): New.
    (set_adm_key): New.
    (do_setattr): New.
    * scd/command.c (MAXLEN_SETATTRDATA): New.
    (cmd_setattr): Add an inquire option.
    
    Signed-off-by: Werner Koch <wk at gnupg.org>

diff --git a/scd/app-piv.c b/scd/app-piv.c
index d984e9c..d34ff7d 100644
--- a/scd/app-piv.c
+++ b/scd/app-piv.c
@@ -112,7 +112,7 @@ static struct data_object_s data_objects[] = {
   { 0x5FC122, 0, 0,0, 1, 0,0, 0, "",   "2.16.23", "SM Cert Signer" },
   { 0x5FC123, 0, 3,3, 1, 0,0, 0, "",   "2.16.24", "Pairing Code Ref Data" },
   { 0 }
-  /* Other key reference values without a tag:
+  /* Other key reference values without a data object:
    * "00" Global PIN (not cleared by application switching)
    * "04" PIV Secure Messaging Key
    * "80" PIV Application PIN
@@ -142,7 +142,7 @@ struct app_local_s {
   /* Various flags.  */
   struct
   {
-    unsigned int dummy:1;
+    unsigned int yubikey:1;  /* This is on a Yubikey.  */
   } flags;
 
 };
@@ -266,6 +266,30 @@ get_cached_data (app_t app, int tag,
 }
 
 
+/* Remove data object described by TAG from the cache.  */
+static void
+flush_cached_data (app_t app, int tag)
+{
+  struct cache_s *c, *cprev;
+
+  for (c=app->app_local->cache, cprev=NULL; c; cprev=c, c = c->next)
+    if (c->tag == tag)
+      {
+        if (cprev)
+          cprev->next = c->next;
+        else
+          app->app_local->cache = c->next;
+        xfree (c);
+
+        for (c=app->app_local->cache; c ; c = c->next)
+          {
+            log_assert (c->tag != tag); /* Oops: duplicated entry. */
+          }
+        return;
+      }
+}
+
+
 /* Get the DO identified by TAG from the card in SLOT and return a
  * buffer with its content in RESULT and NBYTES.  The return value is
  * NULL if not found or a pointer which must be used to release the
@@ -552,6 +576,218 @@ do_getattr (app_t app, ctrl_t ctrl, const char *name)
 }
 
 
+/* Authenticate the card using the Card Application Administration
+ * Key.  (VALUE,VALUELEN) has that 24 byte key.  */
+static gpg_error_t
+auth_adm_key (app_t app, const unsigned char *value, size_t valuelen)
+{
+  gpg_error_t err;
+  unsigned char tmpl[4+24];
+  size_t tmpllen;
+  unsigned char *outdata = NULL;
+  size_t outdatalen;
+  const unsigned char *s;
+  char witness[8];
+  size_t n;
+  gcry_cipher_hd_t cipher = NULL;
+
+  /* Prepare decryption.  */
+  err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
+  if (err)
+    goto leave;
+  err = gcry_cipher_setkey (cipher, value, valuelen);
+  if (err)
+    goto leave;
+
+  /* Request a witness.  */
+  tmpl[0] = 0x7c;
+  tmpl[1] = 0x02;
+  tmpl[2] = 0x80;
+  tmpl[3] = 0;    /* (Empty witness requests a witness.)  */
+  tmpllen = 4;
+  err = iso7816_general_authenticate (app->slot, 0,
+                                      PIV_ALGORITHM_3DES_ECB_0, 0x9B,
+                                      tmpl, tmpllen, 0,
+                                      &outdata, &outdatalen);
+  if (err)
+    goto leave;
+  if (!(outdatalen && *outdata == 0x7c
+        && (s = find_tlv (outdata, outdatalen, 0x80, &n))
+        && n == 8))
+    {
+      err = gpg_error (GPG_ERR_CARD);
+      log_error ("piv: improper witness received\n");
+      goto leave;
+    }
+  err = gcry_cipher_decrypt (cipher, witness, 8, s, 8);
+  if (err)
+    goto leave;
+
+  /* Return decrypted witness and send our challenge.  */
+  tmpl[0] = 0x7c;
+  tmpl[1] = 22;
+  tmpl[2] = 0x80;
+  tmpl[3] = 8;
+  memcpy (tmpl+4, witness, 8);
+  tmpl[12] = 0x81;
+  tmpl[13] = 8;
+  gcry_create_nonce (tmpl+14, 8);
+  tmpl[22] = 0x80;
+  tmpl[23] = 0;
+  tmpllen = 24;
+  xfree (outdata);
+  err = iso7816_general_authenticate (app->slot, 0,
+                                      PIV_ALGORITHM_3DES_ECB_0, 0x9B,
+                                      tmpl, tmpllen, 0,
+                                      &outdata, &outdatalen);
+  if (err)
+    goto leave;
+  if (!(outdatalen && *outdata == 0x7c
+        && (s = find_tlv (outdata, outdatalen, 0x82, &n))
+        && n == 8))
+    {
+      err = gpg_error (GPG_ERR_CARD);
+      log_error ("piv: improper challenge received\n");
+      goto leave;
+    }
+  /* (We reuse the witness buffer.) */
+  err = gcry_cipher_decrypt (cipher, witness, 8, s, 8);
+  if (err)
+    goto leave;
+  if (memcmp (witness, tmpl+14, 8))
+    {
+      err = gpg_error (GPG_ERR_BAD_SIGNATURE);
+      goto leave;
+    }
+
+ leave:
+   xfree (outdata);
+   gcry_cipher_close (cipher);
+   return err;
+}
+
+
+/* Set a new admin key.  */
+static gpg_error_t
+set_adm_key (app_t app, const unsigned char *value, size_t valuelen)
+{
+  gpg_error_t err;
+  unsigned char apdu[8+24];
+  unsigned int sw;
+
+  /* Check whether it is a weak key and that it is of proper length.  */
+  {
+    gcry_cipher_hd_t cipher;
+
+    err = gcry_cipher_open (&cipher, GCRY_CIPHER_3DES, GCRY_CIPHER_MODE_ECB, 0);
+    if (!err)
+      {
+        err = gcry_cipher_setkey (cipher, value, valuelen);
+        gcry_cipher_close (cipher);
+      }
+    if (err)
+      goto leave;
+  }
+
+  if (app->app_local->flags.yubikey)
+    {
+      /* This is a Yubikey.  */
+      if (valuelen != 24)
+        {
+          err = gpg_error (GPG_ERR_INV_LENGTH);
+          goto leave;
+        }
+
+      /* We use a proprietary Yubikey command.  */
+      apdu[0] = 0;
+      apdu[1] = 0xff;
+      apdu[2] = 0xff;
+      apdu[3] = 0xff;  /* touch policy: 0xff=never, 0xfe = always.  */
+      apdu[4] = 3 + 24;
+      apdu[5] = PIV_ALGORITHM_3DES_ECB;
+      apdu[6] = 0x9b;
+      apdu[7] = 24;
+      memcpy (apdu+8, value, 24);
+      err = iso7816_apdu_direct (app->slot, apdu, 8+24, 0, &sw, NULL, NULL);
+      wipememory (apdu+8, 24);
+      if (err)
+        log_error ("piv: setting admin key failed; sw=%04x\n", sw);
+    }
+  else
+    err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+
+ leave:
+   return err;
+}
+
+
+/* Handle the SETATTR operation. All arguments are already basically
+ * checked. */
+static gpg_error_t
+do_setattr (app_t app, const char *name,
+            gpg_error_t (*pincb)(void*, const char *, char **),
+            void *pincb_arg,
+            const unsigned char *value, size_t valuelen)
+{
+  gpg_error_t err;
+  static struct {
+    const char *name;
+    unsigned short tag;
+    unsigned short flush_tag;  /* The tag which needs to be flushed or 0. */
+    int special;               /* Special mode to use for thus NAME.  */
+  } table[] = {
+    /* Authenticate using the PIV Card Application Administration Key
+     * (0x0B).  Note that Yubico calls this key the "management key"
+     * which we don't do because that term is too similar to "Cert
+     * Management Key" (0x9D).  */
+    { "AUTH-ADM-KEY", 0x0000, 0x0000, 1 },
+    { "SET-ADM-KEY",  0x0000, 0x0000, 2 }
+  };
+  int idx;
+
+  (void)pincb;
+  (void)pincb_arg;
+
+  for (idx=0; (idx < DIM (table)
+               && ascii_strcasecmp (table[idx].name, name)); idx++)
+    ;
+  if (!(idx < DIM (table)))
+    return gpg_error (GPG_ERR_INV_NAME);
+
+  /* Flush the cache before writing it, so that the next get operation
+   * will reread the data from the card and thus get synced in case of
+   * errors (e.g. data truncated by the card). */
+  if (table[idx].tag)
+    flush_cached_data (app, table[idx].flush_tag? table[idx].flush_tag
+                       /* */                    : table[idx].tag);
+
+  switch (table[idx].special)
+    {
+    case 0:
+      err = iso7816_put_data (app->slot, 0, table[idx].tag, value, valuelen);
+      if (err)
+        log_error ("failed to set '%s': %s\n",
+                   table[idx].name, gpg_strerror (err));
+      break;
+
+    case 1:
+      err = auth_adm_key (app, value, valuelen);
+      break;
+
+    case 2:
+      err = set_adm_key (app, value, valuelen);
+      break;
+
+    default:
+      err = gpg_error (GPG_ERR_BUG);
+      break;
+    }
+
+  return err;
+}
+
+
 /* Send the KEYPAIRINFO back.  DOBJ describes the data object carrying
  * the key.  This is used by the LEARN command. */
 static gpg_error_t
@@ -1086,13 +1322,15 @@ do_change_pin (app_t app, ctrl_t ctrl, const char *pwidstr,
 
   char *newpin = NULL;
   char *oldpin = NULL;
-  size_t newpinlen;
-  size_t oldpinlen;
-  const char *newdesc;
-  int pwid;
+  /* size_t newpinlen; */
+  /* size_t oldpinlen; */
+  /* const char *newdesc; */
+  /* int pwid; */
   pininfo_t pininfo;
 
   (void)ctrl;
+  (void)pincb;
+  (void)pincb_arg;
 
   /* The minimum and maximum lengths are enforced by PIV.  */
   memset (&pininfo, 0, sizeof pininfo);
@@ -1416,6 +1654,9 @@ app_select_piv (app_t app)
       goto leave;
     }
 
+  if (app->cardtype && !strcmp (app->cardtype, "yubikey"))
+    app->app_local->flags.yubikey = 1;
+
 
   /* FIXME: Parse the optional and conditional DOs in the APT.  */
 
@@ -1427,7 +1668,7 @@ app_select_piv (app_t app)
   app->fnc.readcert = do_readcert;
   app->fnc.readkey = NULL;
   app->fnc.getattr = do_getattr;
-  /* app->fnc.setattr = do_setattr; */
+  app->fnc.setattr = do_setattr;
   /* app->fnc.writecert = do_writecert; */
   /* app->fnc.writekey = do_writekey; */
   /* app->fnc.genkey = do_genkey; */
diff --git a/scd/command.c b/scd/command.c
index 044831f..fb0ba98 100644
--- a/scd/command.c
+++ b/scd/command.c
@@ -55,6 +55,9 @@
 /* Maximum allowed size of certificate data as used in inquiries. */
 #define MAXLEN_CERTDATA 16384
 
+/* Maximum allowed size for "SETATTR --inquire". */
+#define MAXLEN_SETATTRDATA 16384
+
 
 #define set_error(e,t) assuan_set_error (ctx, gpg_error (e), (t))
 
@@ -926,7 +929,7 @@ cmd_getattr (assuan_context_t ctx, char *line)
 
 
 static const char hlp_setattr[] =
-  "SETATTR <name> <value> \n"
+  "SETATTR [--inquire] <name> <value> \n"
   "\n"
   "This command is used to store data on a smartcard.  The allowed\n"
   "names and values are depend on the currently selected smartcard\n"
@@ -935,6 +938,10 @@ static const char hlp_setattr[] =
   "However, the current implementation assumes that NAME is not\n"
   "escaped; this works as long as no one uses arbitrary escaping.\n"
   "\n"
+  "If the option --inquire is used, VALUE shall not be given; instead\n"
+  "an inquiry using the keyword \"VALUE\" is used to retrieve it.  The\n"
+  "value is in this case considered to be confidential and not logged.\n"
+  "\n"
   "A PIN will be requested for most NAMEs.  See the corresponding\n"
   "setattr function of the actually used application (app-*.c) for\n"
   "details.";
@@ -942,14 +949,18 @@ static gpg_error_t
 cmd_setattr (assuan_context_t ctx, char *orig_line)
 {
   ctrl_t ctrl = assuan_get_pointer (ctx);
-  int rc;
+  gpg_error_t err;
   char *keyword;
   int keywordlen;
   size_t nbytes;
   char *line, *linebuf;
+  int opt_inquire;
 
-  if ((rc = open_card (ctrl)))
-    return rc;
+  opt_inquire = has_option (orig_line, "--inquire");
+  orig_line = skip_options (orig_line);
+
+  if ((err = open_card (ctrl)))
+    return err;
 
   /* We need to use a copy of LINE, because PIN_CB uses the same
      context and thus reuses the Assuan provided LINE. */
@@ -964,20 +975,38 @@ cmd_setattr (assuan_context_t ctx, char *orig_line)
       *line++ = 0;
   while (spacep (line))
     line++;
-  nbytes = percent_plus_unescape_inplace (line, 0);
+  if (opt_inquire)
+    {
+      unsigned char *value;
 
-  rc = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx,
-                    (const unsigned char*)line, nbytes);
-  xfree (linebuf);
+      assuan_begin_confidential (ctx);
+      err = assuan_inquire (ctx, "VALUE", &value, &nbytes, MAXLEN_SETATTRDATA);
+      assuan_end_confidential (ctx);
+      if (!err)
+        {
+          err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx,
+                             value, nbytes);
+          wipememory (value, nbytes);
+          xfree (value);
+        }
 
-  return rc;
+   }
+  else
+    {
+      nbytes = percent_plus_unescape_inplace (line, 0);
+      err = app_setattr (ctrl->app_ctx, ctrl, keyword, pin_cb, ctx,
+                         (const unsigned char*)line, nbytes);
+    }
+
+  xfree (linebuf);
+  return err;
 }
 
 
 static const char hlp_writecert[] =
   "WRITECERT <hexified_certid>\n"
   "\n"
-  "This command is used to store a certifciate on a smartcard.  The\n"
+  "This command is used to store a certificate on a smartcard.  The\n"
   "allowed certids depend on the currently selected smartcard\n"
   "application. The actual certifciate is requested using the inquiry\n"
   "\"CERTDATA\" and needs to be provided in its raw (e.g. DER) form.\n"

-----------------------------------------------------------------------

Summary of changes:
 scd/app-piv.c          | 255 +++++++++++++++++++++++++++++++++++++++++++++++--
 scd/command.c          |  49 ++++++++--
 tools/card-tool-misc.c |  35 +++++++
 tools/card-tool.h      |   1 +
 tools/gpg-card-tool.c  |  88 +++++++++++++++--
 5 files changed, 405 insertions(+), 23 deletions(-)


hooks/post-receive
-- 
The GNU Privacy Guard
http://git.gnupg.org




More information about the Gnupg-commits mailing list