- fix 2699
[oweals/gnunet.git] / src / util / crypto_ecc.c
index cfc5deb25277a9637324c0d816fcea8c2e0c4a29..e0764d8d54d8fbef37df8609708c1ae190c4ad90 100644 (file)
  * @file util/crypto_ecc.c
  * @brief public key cryptography (ECC) with libgcrypt
  * @author Christian Grothoff
- *
- * This is just a first, completely untested, draft hack for future ECC support.
- * TODO:
- * - fix public key generation; somehow the result is currently considered invalid by libgcrypt
- *   => suspect that libgcrypt does NOT take pabgn from "CURVE" for public key if not
- *      explicitly given!
- * - actually test it!
  */
 #include "platform.h"
 #include <gcrypt.h>
@@ -177,7 +170,7 @@ GNUNET_CRYPTO_ecc_key_get_public (const struct GNUNET_CRYPTO_EccPrivateKey *priv
  * @return string representing  'pub'
  */
 char *
-GNUNET_CRYPTO_ecc_public_key_to_string (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
+GNUNET_CRYPTO_ecc_public_key_to_string (const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub)
 {
   char *pubkeybuf;
   size_t keylen = (sizeof (struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded)) * 8;
@@ -347,8 +340,9 @@ GNUNET_CRYPTO_ecc_decode_key (const char *buf,
   if (len < sizeof (uint16_t)) 
     return NULL;
   memcpy (&be, buf, sizeof (be));
-  if (len != ntohs (be))
+  if (len < ntohs (be))
     return NULL;
+  len = ntohs (be);
   if (0 != (rc = gcry_sexp_sscan (&sexp,
                                  &erroff,
                                  &buf[2],
@@ -373,8 +367,8 @@ GNUNET_CRYPTO_ecc_decode_key (const char *buf,
  *
  * @return fresh private key
  */
-static struct GNUNET_CRYPTO_EccPrivateKey *
-ecc_key_create ()
+struct GNUNET_CRYPTO_EccPrivateKey *
+GNUNET_CRYPTO_ecc_key_create ()
 {
   struct GNUNET_CRYPTO_EccPrivateKey *ret;
   gcry_sexp_t s_key;
@@ -562,7 +556,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
     }
     LOG (GNUNET_ERROR_TYPE_INFO,
          _("Creating a new private key.  This may take a while.\n"));
-    ret = ecc_key_create ();
+    ret = GNUNET_CRYPTO_ecc_key_create ();
     GNUNET_assert (ret != NULL);
     enc = GNUNET_CRYPTO_ecc_encode_key (ret);
     GNUNET_assert (enc != NULL);
@@ -651,7 +645,7 @@ GNUNET_CRYPTO_ecc_key_create_from_file (const char *filename)
   GNUNET_assert (fs == GNUNET_DISK_file_read (fd, enc, fs));
   len = ntohs (enc->size);
   ret = NULL;
-  if ((len != fs) ||
+  if ((len > fs) ||
       (NULL == (ret = GNUNET_CRYPTO_ecc_decode_key ((char *) enc, len))))
   {
     LOG (GNUNET_ERROR_TYPE_ERROR,
@@ -837,7 +831,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
 {
   struct GNUNET_CRYPTO_EccKeyGenerationContext *gc;
   struct GNUNET_CRYPTO_EccPrivateKey *pk;
-  const char *weak_random;
 
   if (NULL != (pk = try_read_key (filename)))
   {
@@ -865,10 +858,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
     GNUNET_free (gc);
     return NULL;
   }
-  weak_random = NULL;
-  if (GNUNET_YES ==
-      GNUNET_CRYPTO_random_is_weak ())
-    weak_random = "-w";
   gc->gnunet_ecc = GNUNET_OS_start_process (GNUNET_NO,
                                            GNUNET_OS_INHERIT_STD_ERR,
                                            NULL, 
@@ -876,7 +865,6 @@ GNUNET_CRYPTO_ecc_key_create_start (const char *filename,
                                            "gnunet-ecc",
                                            "gnunet-ecc",                                           
                                            gc->filename,
-                                           weak_random,
                                            NULL);
   if (NULL == gc->gnunet_ecc)
   {
@@ -942,8 +930,7 @@ data_to_pkcs1 (const struct GNUNET_CRYPTO_EccSignaturePurpose *purpose)
   gcry_sexp_t data;
 
   GNUNET_CRYPTO_short_hash (purpose, ntohl (purpose->size), &hc);
-#define FORMATSTRING "(4:data(32:01234567890123456789012345678901))"
-#define FORMATSTRING2 "(4:data(4:hash6:sha25632:01234567890123456789012345678901))"
+#define FORMATSTRING "(4:data(5:flags3:raw)(5:value32:01234567890123456789012345678901))"
   bufSize = strlen (FORMATSTRING) + 1;
   {
     char buff[bufSize];
@@ -1059,4 +1046,91 @@ GNUNET_CRYPTO_ecc_verify (uint32_t purpose,
 }
 
 
+/**
+ * Derive key material from a public and a private ECC key.
+ *
+ * @param key private key to use for the ECDH (x)
+ * @param pub public key to use for the ECDY (yG)
+ * @param key_material where to write the key material (xyG)
+ * @return GNUNET_SYSERR on error, GNUNET_OK on success
+ */
+int
+GNUNET_CRYPTO_ecc_ecdh (const struct GNUNET_CRYPTO_EccPrivateKey *key,
+                        const struct GNUNET_CRYPTO_EccPublicKeyBinaryEncoded *pub,
+                        struct GNUNET_HashCode *key_material)
+{ 
+  size_t size;
+  size_t slen;
+  int rc;
+  gcry_sexp_t data;  
+  unsigned char sdata_buf[2048]; /* big enough to print 'sdata' and 'r_sig' */
+
+  /* first, extract the q value from the public key */
+  {
+    gcry_sexp_t psexp;
+    gcry_mpi_t sdata;
+    
+    if (! (psexp = decode_public_key (pub)))
+      return GNUNET_SYSERR;
+    rc = key_from_sexp (&sdata, psexp, "public-key", "q");
+    if (rc)
+      rc = key_from_sexp (&sdata, psexp, "ecc", "q");
+    GNUNET_assert (0 == rc);  
+    gcry_sexp_release (psexp);
+    size = sizeof (sdata_buf);
+    GNUNET_assert (0 ==
+                  gcry_mpi_print (GCRYMPI_FMT_USG, sdata_buf, size, &size,
+                                  sdata));
+    gcry_mpi_release (sdata);
+  }
+  /* convert q value into an S-expression -- whatever format libgcrypt wants,
+     re-using format from sign operation for now... */
+  {
+    char *sexp_string;
+
+#define FORMATPREFIX "(4:data(5:flags3:raw)(5:value%u:"
+#define FORMATPOSTFIX "))"
+    sexp_string = GNUNET_malloc (strlen (FORMATPREFIX) + size + 12 +
+                                 strlen (FORMATPOSTFIX) + 1);
+    GNUNET_snprintf (sexp_string,
+                    strlen (FORMATPREFIX) + 12,
+                    FORMATPREFIX,
+                    size);
+    slen = strlen (sexp_string);
+    memcpy (&sexp_string[slen],
+           sdata_buf, 
+           size);
+    memcpy (&sexp_string[slen + size],
+           FORMATPOSTFIX,
+           strlen (FORMATPOSTFIX) + 1);  
+    GNUNET_assert (0 == gcry_sexp_new (&data, 
+                                      sexp_string, 
+                                      slen + size + strlen (FORMATPOSTFIX), 
+                                      0));
+    GNUNET_free (sexp_string);
+  }
+  /* then call the 'multiply' function, hoping it simply multiplies the points;
+     here we need essentially a WRAPPER around _gcry_mpi_ex_mul_point! - FIXME-WK!*/
+#if WK
+  {
+    gcry_sexp_t result;
+    
+    rc = gcry_ecc_mul_point (&result, data /* scalar */, key->sexp /* point and ctx */);
+    GNUNET_assert (0 == rc);
+    slen = gcry_sexp_sprint (result, GCRYSEXP_FMT_DEFAULT, sdata_buf, sizeof (sdata_buf));
+    GNUNET_assert (0 != slen);
+  }
+#else
+  /* use broken version, insecure! */
+  GNUNET_log (GNUNET_ERROR_TYPE_WARNING, _("To be implemented: not secure at the moment, please read README"));
+  slen = sprintf ((char*) sdata_buf, "FIXME-this is not key material");
+#endif
+  gcry_sexp_release (data);
+
+  /* finally, get a string of the resulting S-expression and hash it to generate the key material */
+  GNUNET_CRYPTO_hash (sdata_buf, slen, key_material);
+  return GNUNET_OK;
+}
+
+
 /* end of crypto_ecc.c */