gpg: Allow decryption using PIV cards.
authorWerner Koch <wk@gnupg.org>
Wed, 3 Apr 2019 13:30:10 +0000 (15:30 +0200)
committerWerner Koch <wk@gnupg.org>
Wed, 3 Apr 2019 13:30:10 +0000 (15:30 +0200)
* g10/call-agent.c (struct getattr_one_parm_s): New.
(getattr_one_status_cb): New.
(agent_scd_getattr_one): New.
* g10/pubkey-enc.c (get_it): Allow the standard leading zero byte from
pkcs#1.
* g10/skclist.c (enum_secret_keys): Handle non-OpenPGP cards.

Signed-off-by: Werner Koch <wk@gnupg.org>
g10/call-agent.c
g10/call-agent.h
g10/pubkey-enc.c
g10/skclist.c

index 3b4882b..f603d49 100644 (file)
@@ -948,6 +948,86 @@ agent_keytocard (const char *hexgrip, int keyno, int force,
 
 
 \f
+/* Object used with the agent_scd_getattr_one.  */
+struct getattr_one_parm_s {
+  const char *keyword;  /* Keyword to look for.  */
+  char *data;           /* Malloced and unescaped data.  */
+  gpg_error_t err;      /* Error code or 0 on success. */
+};
+
+
+/* Callback for agent_scd_getattr_one.  */
+static gpg_error_t
+getattr_one_status_cb (void *opaque, const char *line)
+{
+  struct getattr_one_parm_s *parm = opaque;
+  const char *s;
+
+  if (parm->data)
+    return 0; /* We want only the first occurrence.  */
+
+  if ((s=has_leading_keyword (line, parm->keyword)))
+    {
+      parm->data = percent_plus_unescape (s, 0xff);
+      if (!parm->data)
+        parm->err = gpg_error_from_syserror ();
+    }
+
+  return 0;
+}
+
+
+/* Simplified version of agent_scd_getattr.  This function returns
+ * only the first occurance of the attribute NAME and stores it at
+ * R_VALUE.  A nul in the result is silennly replaced by 0xff.  On
+ * error NULL is stored at R_VALUE.  */
+gpg_error_t
+agent_scd_getattr_one (const char *name, char **r_value)
+{
+  gpg_error_t err;
+  char line[ASSUAN_LINELENGTH];
+  struct default_inq_parm_s inqparm;
+  struct getattr_one_parm_s parm;
+
+  *r_value = NULL;
+
+  if (!*name)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  memset (&inqparm, 0, sizeof inqparm);
+  inqparm.ctx = agent_ctx;
+
+  memset (&parm, 0, sizeof parm);
+  parm.keyword = name;
+
+  /* We assume that NAME does not need escaping. */
+  if (12 + strlen (name) > DIM(line)-1)
+    return gpg_error (GPG_ERR_TOO_LARGE);
+  stpcpy (stpcpy (line, "SCD GETATTR "), name);
+
+  err = start_agent (NULL, 1);
+  if (err)
+    return err;
+
+  err = assuan_transact (agent_ctx, line,
+                         NULL, NULL,
+                         default_inq_cb, &inqparm,
+                         getattr_one_status_cb, &parm);
+  if (!err && parm.err)
+    err = parm.err;
+  else if (!err && !parm.data)
+    err = gpg_error (GPG_ERR_NO_DATA);
+
+  if (!err)
+    *r_value = parm.data;
+  else
+    xfree (parm.data);
+
+  return err;
+}
+
+
+\f
 /* Call the agent to retrieve a data object.  This function returns
  * the data in the same structure as used by the learn command.  It is
  * allowed to update such a structure using this command.
index cb874fd..c0018a5 100644 (file)
@@ -96,6 +96,9 @@ int agent_scd_serialno (char **r_serialno, const char *demand);
 /* Send an APDU to the card.  */
 gpg_error_t agent_scd_apdu (const char *hexapdu, unsigned int *r_sw);
 
+/* Get attribute NAME from the card and store at R_VALUE.  */
+gpg_error_t agent_scd_getattr_one (const char *name, char **r_value);
+
 /* Update INFO with the attribute NAME. */
 int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
 
index 055c39b..f61fa7a 100644 (file)
@@ -319,6 +319,16 @@ get_it (ctrl_t ctrl,
               err = gpg_error (GPG_ERR_WRONG_SECKEY);
               goto leave;
             }
+
+          /* FIXME: Actually the leading zero is required but due to
+           * the way we encode the output in libgcrypt as an MPI we
+           * are not able to encode that leading zero.  However, when
+           * using a Smartcard we are doing it the right way and
+           * therefore we have to skip the zero.  This should be fixed
+           * in gpg-agent of course. */
+          if (!frame[n])
+            n++;
+
           if (frame[n] == 1 && frame[nframe - 1] == 2)
             {
               log_info (_("old encoding of the DEK is not supported\n"));
index ebbaba2..b4f83ea 100644 (file)
@@ -340,6 +340,10 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk)
     SK_LIST results;
   } *c = *context;
 
+#if MAX_FINGERPRINT_LEN < KEYGRIP_LEN
+# error buffer too short for this configuration
+#endif
+
   if (!c)
     {
       /* Make a new context.  */
@@ -430,17 +434,58 @@ enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *sk)
                       xfree (serialno);
                       c->info.fpr2len = 0;
                       err = agent_scd_getattr ("KEY-FPR", &c->info);
+                      if (!err)
+                        {
+                          if (c->info.fpr2len)
+                            {
+                              c->fpr2[0] = '0';
+                              c->fpr2[1] = 'x';
+                              bin2hex (c->info.fpr2, sizeof c->info.fpr2,
+                                       c->fpr2 + 2);
+                              name = c->fpr2;
+                            }
+                        }
+                      else if (gpg_err_code (err) == GPG_ERR_INV_NAME)
+                        {
+                          /* KEY-FPR not supported by the card - get
+                           * the key using the keygrip.  */
+                          char *keyref;
+                          strlist_t kplist, sl;
+                          const char *s;
+                          int i;
+
+                          err = agent_scd_getattr_one ("$ENCRKEYID", &keyref);
+                          if (!err)
+                            {
+                              err = agent_scd_keypairinfo (ctrl, &kplist);
+                              if (!err)
+                                {
+                                  for (sl = kplist; sl; sl = sl->next)
+                                    if ((s = strchr (sl->d, ' '))
+                                        && !strcmp (s+1, keyref))
+                                      break;
+                                  if (sl)
+                                    {
+                                      c->fpr2[0] = '&';
+                                      for (i=1, s=sl->d;
+                                           (*s && *s != ' '
+                                            && i < sizeof c->fpr2 - 3);
+                                           s++, i++)
+                                        c->fpr2[i] = *s;
+                                      c->fpr2[i] = 0;
+                                      name = c->fpr2;
+                                    }
+                                  else /* Restore error.  */
+                                    err = gpg_error (GPG_ERR_INV_NAME);
+                                  free_strlist (kplist);
+                                }
+                            }
+                          xfree (keyref);
+                        }
                       if (err)
-                        log_error ("error retrieving key fingerprint from card: %s\n",
+                        log_error ("error retrieving key from card: %s\n",
                                    gpg_strerror (err));
 
-                      if (c->info.fpr2len)
-                        {
-                          c->fpr2[0] = '0';
-                          c->fpr2[1] = 'x';
-                          bin2hex (c->info.fpr2, sizeof c->info.fpr2,c->fpr2+2);
-                          name = c->fpr2;
-                        }
                       c->sl = c->sl->next;
                     }
                   else