gpg: Improve the keyblock cache's transparency.
[gnupg.git] / common / ssh-utils.c
index e2de802..58586a1 100644 (file)
@@ -3,12 +3,22 @@
  *
  * This file is part of GnuPG.
  *
- * GnuPG is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 3 of the License, or
- * (at your option) any later version.
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of either
  *
- * GnuPG is distributed in the hope that it will be useful,
+ *   - the GNU Lesser General Public License as published by the Free
+ *     Software Foundation; either version 3 of the License, or (at
+ *     your option) any later version.
+ *
+ * or
+ *
+ *   - the GNU General Public License as published by the Free
+ *     Software Foundation; either version 2 of the License, or (at
+ *     your option) any later version.
+ *
+ * or both in parallel, as here.
+ *
+ * This file is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
 #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
    R_FPR.  In case of a error code is returned and NULL stored at
-   R_FPR.  This function is usually called via the ssh_get_fingerprint
-   macro which makes sure to use the correct value for ERRSOURCE. */
+   R_FPR.  */
 static gpg_error_t
-get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
-                 gpg_err_source_t errsource, int as_string)
+get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len, int as_string)
 {
   gpg_error_t err;
   gcry_sexp_t list = NULL;
@@ -45,6 +80,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
   int idx;
   const char *elems;
   gcry_md_hd_t md = NULL;
+  int blobmode = 0;
 
   *r_fpr = NULL;
   *r_len = 0;
@@ -59,7 +95,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
     list = gcry_sexp_find_token (key, "shadowed-private-key", 0);
   if (!list)
     {
-      err = gpg_err_make (errsource, GPG_ERR_UNKNOWN_SEXP);
+      err = gpg_err_make (default_errsource, GPG_ERR_UNKNOWN_SEXP);
       goto leave;
     }
 
@@ -71,7 +107,7 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
   name = gcry_sexp_nth_string (list, 0);
   if (!name)
     {
-      err = gpg_err_make (errsource, GPG_ERR_INV_SEXP);
+      err = gpg_err_make (default_errsource, GPG_ERR_INV_SEXP);
       goto leave;
     }
 
@@ -85,51 +121,118 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
       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_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
+        {
+          /* 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
+            {
+              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\x08nistp384", 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);
+        }
+      break;
+
     default:
       elems = "";
-      err = gpg_err_make (errsource, GPG_ERR_PUBKEY_ALGO);
+      err = gpg_err_make (default_errsource, GPG_ERR_PUBKEY_ALGO);
       break;
     }
   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 (errsource, GPG_ERR_INV_SEXP);
+          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 (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;
+            }
+          blob++;
+          bloblen--;
+          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;
+
+          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);
+          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);
   if (!*r_fpr)
     {
-      err = gpg_err_make (errsource, gpg_err_code_from_syserror ());
+      err = gpg_err_make (default_errsource, gpg_err_code_from_syserror ());
       goto leave;
     }
 
@@ -157,31 +260,25 @@ get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
 /* Return the Secure Shell type fingerprint for KEY.  The length of
    the fingerprint is returned at R_LEN and the fingerprint itself at
    R_FPR.  In case of an error an error code is returned and NULL
-   stored at R_FPR.  This function is usually called via the
-   ssh_get_fingerprint macro which makes sure to use the correct value
-   for ERRSOURCE. */
+   stored at R_FPR.  */
 gpg_error_t
-_ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len,
-                      gpg_err_source_t errsource)
+ssh_get_fingerprint (gcry_sexp_t key, void **r_fpr, size_t *r_len)
 {
-  return get_fingerprint (key, r_fpr, r_len, errsource, 0);
+  return get_fingerprint (key, r_fpr, r_len, 0);
 }
 
 
 /* Return the Secure Shell type fingerprint for KEY as a string.  The
    fingerprint is mallcoed and stored at R_FPRSTR.  In case of an
-   error an error code is returned and NULL stored at R_FPRSTR.  This
-   function is usually called via the ssh_get_fingerprint_string macro
-   which makes sure to use the correct value for ERRSOURCE. */
+   error an error code is returned and NULL stored at R_FPRSTR.  */
 gpg_error_t
-_ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr,
-                             gpg_err_source_t errsource)
+ssh_get_fingerprint_string (gcry_sexp_t key, char **r_fprstr)
 {
   gpg_error_t err;
   size_t dummy;
   void *string;
 
-  err = get_fingerprint (key, &string, &dummy, errsource, 1);
+  err = get_fingerprint (key, &string, &dummy, 1);
   *r_fprstr = string;
   return err;
 }