agent: Cleanups to prepare implementation of Ed25519.
authorWerner Koch <wk@gnupg.org>
Sat, 22 Mar 2014 19:51:16 +0000 (20:51 +0100)
committerWerner Koch <wk@gnupg.org>
Sat, 22 Mar 2014 19:52:03 +0000 (20:52 +0100)
* agent/cvt-openpgp.c: Remove.
(convert_to_openpgp): Use gcry_sexp_extract_param.
* agent/findkey.c (is_eddsa): New.
(agent_is_dsa_key, agent_is_eddsa_key): Check whether ecc means EdDSA.
* agent/pksign.c (agent_pksign_do): Add args OVERRIDEDATA and
OVERRIDEDATALEN.

* common/ssh-utils.c (is_eddsa): New.
(get_fingerprint): Take care or EdDSA.

agent/agent.h
agent/command-ssh.c
agent/command.c
agent/cvt-openpgp.c
agent/findkey.c
agent/pksign.c
common/ssh-utils.c

index eac7ba5..58e5841 100644 (file)
@@ -368,7 +368,8 @@ char *agent_get_cache (const char *key, cache_mode_t cache_mode);
 int agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                      const char *desc_text,
                     gcry_sexp_t *signature_sexp,
-                     cache_mode_t cache_mode, lookup_ttl_t lookup_ttl);
+                     cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+                     const void *overridedata, size_t overridedatalen);
 int agent_pksign (ctrl_t ctrl, const char *cache_nonce,
                   const char *desc_text,
                   membuf_t *outbuf, cache_mode_t cache_mode);
index 4191d6f..fb3b29a 100644 (file)
@@ -2515,7 +2515,8 @@ data_sign (ctrl_t ctrl, ssh_key_type_spec_t *spec,
                          _("Please enter the passphrase "
                            "for the ssh key%%0A  %F%%0A  (%c)"),
                          &signature_sexp,
-                         CACHE_MODE_SSH, ttl_from_sshcontrol);
+                         CACHE_MODE_SSH, ttl_from_sshcontrol,
+                         NULL, 0);
   ctrl->use_auth_call = 0;
   if (err)
     goto out;
index d1e53cd..fab27f0 100644 (file)
@@ -2147,7 +2147,7 @@ cmd_export_key (assuan_context_t ctx, char *line)
 
   if (!ctrl->server_local->export_key)
     {
-      err = gpg_error (GPG_ERR_MISSING_KEY);
+      err = set_error (GPG_ERR_MISSING_KEY, "did you run KEYWRAP_KEY");
       goto leave;
     }
 
index 205b953..5718bd9 100644 (file)
@@ -1030,46 +1030,6 @@ convert_from_openpgp_native (ctrl_t ctrl,
 }
 
 
-\f
-static gpg_error_t
-key_from_sexp (gcry_sexp_t sexp, const char *elems, gcry_mpi_t *array)
-{
-  gpg_error_t err = 0;
-  gcry_sexp_t l2;
-  int idx;
-
-  for (idx=0; *elems; elems++, idx++)
-    {
-      l2 = gcry_sexp_find_token (sexp, elems, 1);
-      if (!l2)
-        {
-          err = gpg_error (GPG_ERR_NO_OBJ); /* Required parameter not found.  */
-          goto leave;
-        }
-      array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      if (!array[idx])
-        {
-          err = gpg_error (GPG_ERR_INV_OBJ); /* Required parameter invalid.  */
-          goto leave;
-        }
-    }
-
- leave:
-  if (err)
-    {
-      int i;
-
-      for (i=0; i < idx; i++)
-        {
-          gcry_mpi_release (array[i]);
-          array[i] = NULL;
-        }
-    }
-  return err;
-}
-
-
 /* Given an ARRAY of mpis with the key parameters, protect the secret
    parameters in that array and replace them by one opaque encoded
    mpi.  NPKEY is the number of public key parameters and NSKEY is
@@ -1173,7 +1133,6 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
   gpg_error_t err;
   gcry_sexp_t list, l2;
   char *name;
-  int algo;
   const char *algoname;
   const char *elems;
   int npkey, nskey;
@@ -1203,26 +1162,63 @@ convert_to_openpgp (ctrl_t ctrl, gcry_sexp_t s_key, const char *passphrase,
       return gpg_error (GPG_ERR_INV_OBJ); /* Invalid structure of object. */
     }
 
-  algo = gcry_pk_map_name (name);
-  xfree (name);
-
-  switch (algo)
+  /* Map NAME to a name as used by Libgcrypt.  We do not use the
+     Libgcrypt function here because we need a lowercase name and
+     require special treatment for some algorithms.  */
+  strlwr (name);
+  if (!strcmp (name, "rsa"))
+    {
+      algoname = "rsa";
+      npkey = 2;
+      elems = "nedpqu";
+    }
+  else if (!strcmp (name, "elg"))
+    {
+      algoname = "elg";
+      npkey = 3;
+      elems = "pgyx";
+    }
+  else if (!strcmp (name, "dsa"))
+    {
+      algoname = "dsa";
+      npkey = 4;
+      elems = "pqgyx";
+    }
+  else if (!strcmp (name, "ecc"))
+    {
+      algoname = "?"; /* Decide later by checking the usage.  */
+      npkey = 6;
+      elems = "pabgnqd";
+    }
+  else if (!strcmp (name, "ecdsa"))
+    {
+      algoname = "ecdsa";
+      npkey = 6;
+      elems = "pabgnqd";
+    }
+  else if (!strcmp (name, "ecdh"))
     {
-    case GCRY_PK_RSA:   algoname = "rsa";   npkey = 2; elems = "nedpqu";  break;
-    case GCRY_PK_ELG:   algoname = "elg";   npkey = 3; elems = "pgyx";    break;
-    case GCRY_PK_ELG_E: algoname = "elg";   npkey = 3; elems = "pgyx";    break;
-    case GCRY_PK_DSA:   algoname = "dsa";   npkey = 4; elems = "pqgyx";   break;
-    case GCRY_PK_ECDSA: algoname = "ecdsa"; npkey = 6; elems = "pabgnqd"; break;
-    case GCRY_PK_ECDH:  algoname = "ecdh";  npkey = 6; elems = "pabgnqd"; break;
-    default:            algoname = "";      npkey = 0; elems = NULL;      break;
+      algoname = "ecdh";
+      npkey = 6;
+      elems = "pabgnqd";
     }
+  else
+    {
+      algoname = "";
+      npkey = 0;
+      elems = NULL;
+    }
+  xfree (name);
   assert (!elems || strlen (elems) < DIM (array) );
   nskey = elems? strlen (elems) : 0;
 
+  /* Extract the parameters and put them into an array.  */
   if (!elems)
     err = gpg_error (GPG_ERR_PUBKEY_ALGO);
   else
-    err = key_from_sexp (list, elems, array);
+    err = gcry_sexp_extract_param (list, NULL, elems,
+                                   array+0, array+1, array+2, array+3, array+4,
+                                   array+5, array+6, NULL);
   gcry_sexp_release (list);
   if (err)
     return err;
index 7b24c55..84d2cfd 100644 (file)
@@ -688,7 +688,7 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce,
    string describing the names of the parameters.  ALGONAMESIZE and
    ELEMSSIZE give the allocated size of the provided buffers.  The
    buffers may be NULL if not required.  If R_LIST is not NULL the top
-   level list will be stored tehre; the caller needs to release it in
+   level list will be stored there; the caller needs to release it in
    this case.  */
 static gpg_error_t
 key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
@@ -776,27 +776,65 @@ key_parms_from_sexp (gcry_sexp_t s_key, gcry_sexp_t *r_list,
 }
 
 
+/* Return true if KEYPARMS holds an EdDSA key.  */
+static int
+is_eddsa (gcry_sexp_t keyparms)
+{
+  int result = 0;
+  gcry_sexp_t list;
+  const char *s;
+  size_t n;
+  int i;
+
+  list = gcry_sexp_find_token (keyparms, "flags", 0);
+  for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
+    {
+      s = gcry_sexp_nth_data (list, i, &n);
+      if (!s)
+        continue; /* Not a data element. */
+
+      if (n == 5 && !memcmp (s, "eddsa", 5))
+        {
+          result = 1;
+          break;
+        }
+    }
+  gcry_sexp_release (list);
+  return result;
+}
+
+
 /* Return the public key algorithm number if S_KEY is a DSA style key.
    If it is not a DSA style key, return 0.  */
 int
 agent_is_dsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an DSA key.  */
 
   if (!strcmp (algoname, "dsa"))
-    return GCRY_PK_DSA;
+    result = GCRY_PK_DSA;
   else if (!strcmp (algoname, "ecc"))
-    return GCRY_PK_ECDSA; /* FIXME: Check for the EdDSA flag.  */
+    {
+      if (is_eddsa (list))
+        result = 0;
+      else
+        result = GCRY_PK_ECDSA;
+    }
   else if (!strcmp (algoname, "ecdsa"))
-    return GCRY_PK_ECDSA;
+    result = GCRY_PK_ECDSA;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
@@ -804,18 +842,25 @@ agent_is_dsa_key (gcry_sexp_t s_key)
 int
 agent_is_eddsa_key (gcry_sexp_t s_key)
 {
+  int result;
+  gcry_sexp_t list;
   char algoname[6];
 
   if (!s_key)
     return 0;
 
-  if (key_parms_from_sexp (s_key, NULL, algoname, sizeof algoname, NULL, 0))
+  if (key_parms_from_sexp (s_key, &list, algoname, sizeof algoname, NULL, 0))
     return 0; /* Error - assume it is not an EdDSA key.  */
 
-  if (!strcmp (algoname, "eddsa"))
-    return 1;
+  if (!strcmp (algoname, "ecc") && is_eddsa (list))
+    result = 1;
+  else if (!strcmp (algoname, "eddsa")) /* backward compatibility.  */
+    result = 1;
   else
-    return 0;
+    result = 0;
+
+  gcry_sexp_release (list);
+  return result;
 }
 
 
index 0886150..fb593a6 100644 (file)
@@ -276,18 +276,35 @@ do_encode_raw_pkcs1 (const byte *md, size_t mdlen, unsigned int nbits,
    the signature S-expression.  LOOKUP is an optional function to
    provide a way for lower layers to ask for the caching TTL.  If a
    CACHE_NONCE is given that cache item is first tried to get a
-   passphrase.  */
+   passphrase.  If OVERRIDEDATA is not NULL, OVERRIDEDATALEN bytes
+   from this buffer are used instead of the data in CTRL.  The
+   override feature is required to allow the use of Ed25519 with ssh
+   because Ed25519 dies the hashing itself.  */
 int
 agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
                  const char *desc_text,
                 gcry_sexp_t *signature_sexp,
-                 cache_mode_t cache_mode, lookup_ttl_t lookup_ttl)
+                 cache_mode_t cache_mode, lookup_ttl_t lookup_ttl,
+                 const void *overridedata, size_t overridedatalen)
 {
   gcry_sexp_t s_skey = NULL, s_sig = NULL;
   unsigned char *shadow_info = NULL;
   unsigned int rc = 0;         /* FIXME: gpg-error? */
+  const unsigned char *data;
+  int datalen;
 
-  if (! ctrl->have_keygrip)
+  if (overridedata)
+    {
+      data = overridedata;
+      datalen = overridedatalen;
+    }
+  else
+    {
+      data = ctrl->digest.value;
+      datalen = ctrl->digest.valuelen;
+    }
+
+  if (!ctrl->have_keygrip)
     return gpg_error (GPG_ERR_NO_SECKEY);
 
   rc = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip,
@@ -315,8 +332,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
         is_ECDSA = 1;
 
       rc = divert_pksign (ctrl,
-                          ctrl->digest.value,
-                          ctrl->digest.valuelen,
+                          data, datalen,
                           ctrl->digest.algo,
                           shadow_info, &buf, &len);
       if (rc)
@@ -405,22 +421,18 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce,
 
       /* Put the hash into a sexp */
       if (agent_is_eddsa_key (s_skey))
-        rc = do_encode_eddsa (ctrl->digest.value,
-                              ctrl->digest.valuelen,
+        rc = do_encode_eddsa (data, datalen,
                               &s_hash);
       else if (ctrl->digest.algo == MD_USER_TLS_MD5SHA1)
-        rc = do_encode_raw_pkcs1 (ctrl->digest.value,
-                                  ctrl->digest.valuelen,
+        rc = do_encode_raw_pkcs1 (data, datalen,
                                   gcry_pk_get_nbits (s_skey),
                                   &s_hash);
       else if ( (dsaalgo = agent_is_dsa_key (s_skey)) )
-        rc = do_encode_dsa (ctrl->digest.value,
-                            ctrl->digest.valuelen,
+        rc = do_encode_dsa (data, datalen,
                             dsaalgo, s_skey,
                             &s_hash);
       else
-        rc = do_encode_md (ctrl->digest.value,
-                           ctrl->digest.valuelen,
+        rc = do_encode_md (data, datalen,
                            ctrl->digest.algo,
                            &s_hash,
                            ctrl->digest.raw_value);
@@ -468,7 +480,8 @@ agent_pksign (ctrl_t ctrl, const char *cache_nonce, const char *desc_text,
   size_t len = 0;
   int rc = 0;
 
-  rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL);
+  rc = agent_pksign_do (ctrl, cache_nonce, desc_text, &s_sig, cache_mode, NULL,
+                        NULL, 0);
   if (rc)
     goto leave;
 
index 0c71567..a75b3c0 100644 (file)
 #include "ssh-utils.h"
 
 
+/* Return true if KEYPARMS holds an EdDSA key.  */
+static int
+is_eddsa (gcry_sexp_t keyparms)
+{
+  int result = 0;
+  gcry_sexp_t list;
+  const char *s;
+  size_t n;
+  int i;
+
+  list = gcry_sexp_find_token (keyparms, "flags", 0);
+  for (i = list ? gcry_sexp_length (list)-1 : 0; i > 0; i--)
+    {
+      s = gcry_sexp_nth_data (list, i, &n);
+      if (!s)
+        continue; /* Not a data element. */
+
+      if (n == 5 && !memcmp (s, "eddsa", 5))
+        {
+          result = 1;
+          break;
+        }
+    }
+  gcry_sexp_release (list);
+  return result;
+}
+
 
 /* Return the Secure Shell type fingerprint for KEY.  The length of
    the fingerprint is returned at R_LEN and the fingerprint itself at
@@ -53,6 +80,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
   int idx;
   const char *elems;
   gcry_md_hd_t md = NULL;
+  int blobmode = 0;
 
   *r_fpr = NULL;
   *r_len = 0;
@@ -93,38 +121,52 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
       elems = "en";
       gcry_md_write (md, "\0\0\0\x07ssh-rsa", 11);
       break;
+
     case GCRY_PK_DSA:
       elems = "pqgy";
       gcry_md_write (md, "\0\0\0\x07ssh-dss", 11);
       break;
-    case GCRY_PK_ECDSA:
-      /* We only support the 3 standard curves for now.  It is just a
-         quick hack.  */
-      elems = "q";
-      gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
-      l2 = gcry_sexp_find_token (list, "curve", 0);
-      if (!l2)
-        elems = "";
+
+    case GCRY_PK_ECC:
+      if (is_eddsa (list))
+        {
+          elems = "q";
+          blobmode = 1;
+          /* For now there is just one curve, thus no need to switch
+             on it.  */
+          gcry_md_write (md, "\0\0\0\x0b" "ssh-ed25519", 15);
+        }
       else
         {
-          gcry_free (name);
-          name = gcry_sexp_nth_string (l2, 1);
-          gcry_sexp_release (l2);
-          l2 = NULL;
-          if (!name)
+          /* We only support the 3 standard curves for now.  It is
+             just a quick hack.  */
+          elems = "q";
+          gcry_md_write (md, "\0\0\0\x13" "ecdsa-sha2-nistp", 20);
+          l2 = gcry_sexp_find_token (list, "curve", 0);
+          if (!l2)
             elems = "";
-          else if (!strcmp (name, "NIST P-256") || !strcmp (name, "nistp256"))
-            gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
-          else if (!strcmp (name, "NIST P-384") || !strcmp (name, "nistp384"))
-            gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
-          else if (!strcmp (name, "NIST P-521") || !strcmp (name, "nistp521"))
-            gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
           else
-            elems = "";
+            {
+              gcry_free (name);
+              name = gcry_sexp_nth_string (l2, 1);
+              gcry_sexp_release (l2);
+              l2 = NULL;
+              if (!name)
+                elems = "";
+              else if (!strcmp (name, "NIST P-256")||!strcmp (name, "nistp256"))
+                gcry_md_write (md, "256\0\0\0\x08nistp256", 15);
+              else if (!strcmp (name, "NIST P-384")||!strcmp (name, "nistp384"))
+                gcry_md_write (md, "384\0\0\0\x08nistp521", 15);
+              else if (!strcmp (name, "NIST P-521")||!strcmp (name, "nistp521"))
+                gcry_md_write (md, "521\0\0\0\x08nistp521", 15);
+              else
+                elems = "";
+            }
+          if (!*elems)
+            err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
         }
-      if (!*elems)
-        err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_CURVE);
       break;
+
     default:
       elems = "";
       err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
@@ -133,33 +175,56 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
   if (err)
     goto leave;
 
+
   for (idx = 0, s = elems; *s; s++, idx++)
     {
-      gcry_mpi_t a;
-      unsigned char *buf;
-      size_t buflen;
-
       l2 = gcry_sexp_find_token (list, s, 1);
       if (!l2)
         {
           err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
           goto leave;
         }
-      a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
-      gcry_sexp_release (l2);
-      l2 = NULL;
-      if (!a)
+      if (blobmode)
         {
-          err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
-          goto leave;
+          const char *blob;
+          size_t bloblen;
+          unsigned char lenbuf[4];
+
+          blob = gcry_sexp_nth_data (l2, 1, &bloblen);
+          if (!blob)
+            {
+              err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
+              goto leave;
+            }
+          lenbuf[0] = bloblen >> 24;
+          lenbuf[1] = bloblen >> 16;
+          lenbuf[2] = bloblen >>  8;
+          lenbuf[3] = bloblen;
+          gcry_md_write (md, lenbuf, 4);
+          gcry_md_write (md, blob, bloblen);
         }
+      else
+        {
+          gcry_mpi_t a;
+          unsigned char *buf;
+          size_t buflen;
 
-      err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
-      gcry_mpi_release (a);
-      if (err)
-        goto leave;
-      gcry_md_write (md, buf, buflen);
-      gcry_free (buf);
+          a = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG);
+          gcry_sexp_release (l2);
+          l2 = NULL;
+          if (!a)
+            {
+              err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
+              goto leave;
+            }
+
+          err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a);
+          gcry_mpi_release (a);
+          if (err)
+            goto leave;
+          gcry_md_write (md, buf, buflen);
+          gcry_free (buf);
+        }
     }
 
   *r_fpr = gcry_malloc (as_string? 61:20);