ecc: Fix X25519 computation on Curve25519.
authorNIIBE Yutaka <gniibe@fsij.org>
Tue, 12 Apr 2016 00:58:12 +0000 (09:58 +0900)
committerNIIBE Yutaka <gniibe@fsij.org>
Tue, 12 Apr 2016 00:58:12 +0000 (09:58 +0900)
* cipher/ecc.c (ecc_encrypt_raw): Tweak of bits when
PUBKEY_FLAG_DJB_TWEAK is enabled.
(ecc_decrypt_raw): Return 0 when PUBKEY_FLAG_DJB_TWEAK is enabled.
* tests/t-cv25519.c (test_cv): Update by using gcry_pk_encrypt.

--

X25519 function is not a plain scalar multiplication, but does
two things; the scalar bits are tweaked before applying scalar
multiplication and X0 function is applied to the result of
scalar multiplication.

In libgcrypt, _gcry_mpi_ec_mul_point is a plain scalar multiplication
and those two things are done in functions for ECDH with X25519.

Signed-off-by: NIIBE Yutaka <gniibe@fsij.org>
cipher/ecc.c
tests/t-cv25519.c

index 7ab7244..f6b2b69 100644 (file)
@@ -1282,15 +1282,12 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
   rc = _gcry_pk_util_data_to_mpi (s_data, &data, &ctx);
   if (rc)
     goto leave;
-  if (DBG_CIPHER)
-    log_mpidump ("ecc_encrypt data", data);
   if (mpi_is_opaque (data))
     {
       rc = GPG_ERR_INV_DATA;
       goto leave;
     }
 
-
   /*
    * Extract the key.
    */
@@ -1327,6 +1324,21 @@ ecc_encrypt_raw (gcry_sexp_t *r_ciph, gcry_sexp_t s_data, gcry_sexp_t keyparms)
       pk.E.dialect = ECC_DIALECT_STANDARD;
     }
 
+  /*
+   * Tweak the scalar bits by cofactor and number of bits of the field.
+   * It assumes the cofactor is a power of 2.
+   */
+  if ((flags & PUBKEY_FLAG_DJB_TWEAK))
+    {
+      int i;
+
+      for (i = 0; i < mpi_get_nbits (pk.E.h) - 1; i++)
+        mpi_clear_bit (data, i);
+      mpi_set_highbit (data, mpi_get_nbits (pk.E.p) - 1);
+    }
+  if (DBG_CIPHER)
+    log_mpidump ("ecc_encrypt data", data);
+
   if (DBG_CIPHER)
     {
       log_debug ("ecc_encrypt info: %s/%s\n",
@@ -1607,7 +1619,13 @@ ecc_decrypt_raw (gcry_sexp_t *r_plain, gcry_sexp_t s_data, gcry_sexp_t keyparms)
     else
       y = mpi_new (0);
 
-    if (_gcry_mpi_ec_get_affine (x, y, &R, ec))
+    /*
+     * Here, x is 0.  In the X25519 computation on Curve25519, X0
+     * function maps infinity to zero.  So, when PUBKEY_FLAG_DJB_TWEAK
+     * is enabled, we can just skip the check to get the result of 0.
+     */
+    if (_gcry_mpi_ec_get_affine (x, y, &R, ec)
+        && !(flags & PUBKEY_FLAG_DJB_TWEAK))
       log_fatal ("ecdh: Failed to get affine coordinates\n");
 
     if (y)
index 69aa005..7e551c2 100644 (file)
@@ -192,8 +192,8 @@ test_cv (int testno, const char *k_str, const char *u_str,
   gpg_error_t err;
   void *buffer = NULL;
   size_t buflen;
-  unsigned char *p;
-  gcry_sexp_t s_sk = NULL;
+  gcry_sexp_t s_pk = NULL;
+  gcry_mpi_t mpi_k = NULL;
   gcry_sexp_t s_data = NULL;
   gcry_sexp_t s_result = NULL;
   gcry_sexp_t s_tmp = NULL;
@@ -210,29 +210,22 @@ test_cv (int testno, const char *k_str, const char *u_str,
       goto leave;
     }
 
-  /*
-   * Do what decodeScalar25519 does.
-   */
-  p = (unsigned char *)buffer;
-  p[0] &= 248;
-  p[31] &= 127;
-  p[31] |= 64;
   reverse_buffer (buffer, buflen);
+  if ((err = gcry_mpi_scan (&mpi_k, GCRYMPI_FMT_USG, buffer, buflen, NULL)))
+    {
+      fail ("error converting MPI for test %d: %s", testno, gpg_strerror (err));
+      goto leave;
+    }
 
-  if ((err = gcry_sexp_build (&s_sk, NULL,
-                              "(private-key"
-                              " (ecc"
-                              "  (curve \"Curve25519\")"
-                              "  (flags djb-tweak)"
-                              "  (d %b)))", (int)buflen, buffer)))
+  if ((err = gcry_sexp_build (&s_data, NULL, "%m", mpi_k)))
     {
       fail ("error building s-exp for test %d, %s: %s",
-            testno, "sk", gpg_strerror (err));
+            testno, "data", gpg_strerror (err));
       goto leave;
     }
 
   xfree (buffer);
-  if (!(buffer = hex2buffer (u_str, &buflen)))
+  if (!(buffer = hex2buffer (u_str, &buflen)) || buflen != 32)
     {
       fail ("error building s-exp for test %d, %s: %s",
             testno, "u", "invalid hex string");
@@ -247,26 +240,28 @@ test_cv (int testno, const char *k_str, const char *u_str,
    * We could add the prefix 0x40, but libgcrypt also supports
    * format with no prefix.  So, it is OK not to put the prefix.
    */
-  if ((err = gcry_sexp_build (&s_data, NULL,
-                              "(enc-val"
-                              " (ecdh"
-                              "  (e %b)))", (int)buflen, buffer)))
+  if ((err = gcry_sexp_build (&s_pk, NULL,
+                              "(public-key"
+                              " (ecc"
+                              "  (curve \"Curve25519\")"
+                              "  (flags djb-tweak)"
+                              "  (q%b)))", (int)buflen, buffer)))
     {
       fail ("error building s-exp for test %d, %s: %s",
-            testno, "data", gpg_strerror (err));
+            testno, "pk", gpg_strerror (err));
       goto leave;
     }
 
   xfree (buffer);
   buffer = NULL;
 
-  if ((err = gcry_pk_decrypt (&s_result, s_data, s_sk)))
-    fail ("gcry_pk_decrypt failed for test %d: %s", testno,
+  if ((err = gcry_pk_encrypt (&s_result, s_data, s_pk)))
+    fail ("gcry_pk_encrypt failed for test %d: %s", testno,
           gpg_strerror (err));
 
-  s_tmp = gcry_sexp_find_token (s_result, "value", 0);
+  s_tmp = gcry_sexp_find_token (s_result, "s", 0);
   if (!s_tmp || !(res = gcry_sexp_nth_buffer (s_tmp, 1, &res_len)))
-    fail ("gcry_pk_decrypt failed for test %d: %s", testno, "missing value");
+    fail ("gcry_pk_encrypt failed for test %d: %s", testno, "missing value");
   else
     {
       char *r, *r0;
@@ -275,16 +270,16 @@ test_cv (int testno, const char *k_str, const char *u_str,
       /* To skip the prefix 0x40, for-loop start with i=1 */
       r0 = r = xmalloc (2*(res_len)+1);
       if (!r0)
-       {
-         fail ("memory allocation", testno);
-         goto leave;
-       }
+        {
+          fail ("memory allocation", testno);
+          goto leave;
+        }
 
       for (i=1; i < res_len; i++, r += 2)
         snprintf (r, 3, "%02x", res[i]);
       if (strcmp (result_str, r0))
         {
-          fail ("gcry_pk_decrypt failed for test %d: %s",
+          fail ("gcry_pk_encrypt failed for test %d: %s",
                 testno, "wrong value returned");
           show ("  expected: '%s'", result_str);
           show ("       got: '%s'", r0);
@@ -294,10 +289,11 @@ test_cv (int testno, const char *k_str, const char *u_str,
 
  leave:
   xfree (res);
+  gcry_mpi_release (mpi_k);
   gcry_sexp_release (s_tmp);
   gcry_sexp_release (s_result);
   gcry_sexp_release (s_data);
-  gcry_sexp_release (s_sk);
+  gcry_sexp_release (s_pk);
   xfree (buffer);
 }
 
@@ -370,7 +366,7 @@ test_it (int testno, const char *k_str, int iter, const char *result_str)
       gcry_mpi_ec_get_affine (mpi_k, NULL, Q, ctx);
 
       if (debug)
-       print_mpi ("k", mpi_k);
+        print_mpi ("k", mpi_k);
     }
 
   {
@@ -383,8 +379,8 @@ test_it (int testno, const char *k_str, int iter, const char *result_str)
     r0 = r = xmalloc (65);
     if (!r0)
       {
-       fail ("memory allocation", testno);
-       goto leave;
+        fail ("memory allocation", testno);
+        goto leave;
       }
 
     for (i=0; i < 32; i++, r += 2)
@@ -395,7 +391,7 @@ test_it (int testno, const char *k_str, int iter, const char *result_str)
         fail ("curv25519 failed for test %d: %s",
               testno, "wrong value returned");
         show ("  expected: '%s'", result_str);
-       show ("       got: '%s'", r0);
+        show ("       got: '%s'", r0);
       }
     xfree (r0);
   }