mpi/ec: fix when 'unsigned long' is 32-bit but limb size is 64-bit
[libgcrypt.git] / cipher / ecc-misc.c
index 26c9e8d..41debe4 100644 (file)
@@ -43,6 +43,7 @@ _gcry_ecc_curve_free (elliptic_curve_t *E)
   mpi_free (E->b);  E->b = NULL;
   _gcry_mpi_point_free_parts (&E->G);
   mpi_free (E->n);  E->n = NULL;
+  mpi_free (E->h);  E->h = NULL;
 }
 
 
@@ -63,6 +64,7 @@ _gcry_ecc_curve_copy (elliptic_curve_t E)
   _gcry_mpi_point_init (&R.G);
   point_set (&R.G, &E.G);
   R.n = mpi_copy (E.n);
+  R.h = mpi_copy (E.h);
 
   return R;
 }
@@ -79,7 +81,7 @@ _gcry_ecc_model2str (enum gcry_mpi_ec_models model)
     {
     case MPI_EC_WEIERSTRASS:    str = "Weierstrass"; break;
     case MPI_EC_MONTGOMERY:     str = "Montgomery";  break;
-    case MPI_EC_TWISTEDEDWARDS: str = "Twisted Edwards"; break;
+    case MPI_EC_EDWARDS:        str = "Edwards"; break;
     }
   return str;
 }
@@ -110,7 +112,7 @@ _gcry_ecc_ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
   unsigned char *buf, *ptr;
   gcry_mpi_t result;
 
-  buf = gcry_xmalloc ( 1 + 2*pbytes );
+  buf = xmalloc ( 1 + 2*pbytes );
   *buf = 04; /* Uncompressed point.  */
   ptr = buf+1;
   rc = _gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
@@ -134,7 +136,7 @@ _gcry_ecc_ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
   rc = _gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL);
   if (rc)
     log_fatal ("mpi_scan failed: %s\n", gpg_strerror (rc));
-  gcry_free (buf);
+  xfree (buf);
 
   return result;
 }
@@ -185,11 +187,11 @@ _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value)
   else
     {
       n = (mpi_get_nbits (value)+7)/8;
-      buf_memory= gcry_xmalloc (n);
+      buf_memory = xmalloc (n);
       rc = _gcry_mpi_print (GCRYMPI_FMT_USG, buf_memory, n, &n, value);
       if (rc)
         {
-          gcry_free (buf_memory);
+          xfree (buf_memory);
           return rc;
         }
       buf = buf_memory;
@@ -197,28 +199,28 @@ _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value)
 
   if (n < 1)
     {
-      gcry_free (buf_memory);
+      xfree (buf_memory);
       return GPG_ERR_INV_OBJ;
     }
   if (*buf != 4)
     {
-      gcry_free (buf_memory);
+      xfree (buf_memory);
       return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression.  */
     }
   if ( ((n-1)%2) )
     {
-      gcry_free (buf_memory);
+      xfree (buf_memory);
       return GPG_ERR_INV_OBJ;
     }
   n = (n-1)/2;
   rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
   if (rc)
     {
-      gcry_free (buf_memory);
+      xfree (buf_memory);
       return rc;
     }
   rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
-  gcry_free (buf_memory);
+  xfree (buf_memory);
   if (rc)
     {
       mpi_free (x);
@@ -236,20 +238,6 @@ _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value)
 }
 
 
-static void
-reverse_buffer (unsigned char *buffer, unsigned int length)
-{
-  unsigned int tmp, i;
-
-  for (i=0; i < length/2; i++)
-    {
-      tmp = buffer[i];
-      buffer[i] = buffer[length-1-i];
-      buffer[length-1-i] = tmp;
-    }
-}
-
-
 /* Compute the public key from the the context EC.  Obviously a
    requirement is that the secret key is available in EC.  On success
    Q is returned; on error NULL.  If Q is NULL a newly allocated point
@@ -259,8 +247,6 @@ mpi_point_t
 _gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec,
                           mpi_point_t G, gcry_mpi_t d)
 {
-  int rc;
-
   if (!G)
     G = ec->G;
   if (!d)
@@ -268,51 +254,21 @@ _gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec,
 
   if (!d || !G || !ec->p || !ec->a)
     return NULL;
-  if (ec->model == MPI_EC_TWISTEDEDWARDS && !ec->b)
+  if (ec->model == MPI_EC_EDWARDS && !ec->b)
     return NULL;
 
   if (ec->dialect == ECC_DIALECT_ED25519
       && (ec->flags & PUBKEY_FLAG_EDDSA))
     {
       gcry_mpi_t a;
-      unsigned char *rawmpi = NULL;
-      unsigned int rawmpilen;
       unsigned char *digest;
-      gcry_buffer_t hvec[2];
-      int b = (ec->nbits+7)/8;
 
-      gcry_assert (b >= 32);
-      digest = gcry_calloc_secure (2, b);
-      if (!digest)
+      if (_gcry_ecc_eddsa_compute_h_d (&digest, d, ec))
         return NULL;
-      memset (hvec, 0, sizeof hvec);
 
-      rawmpi = _gcry_mpi_get_buffer (d, 0, &rawmpilen, NULL);
-      if (!rawmpi)
-        return NULL;
-      memset (digest, 0, b);
-      hvec[0].data = digest;
-      hvec[0].off = 0;
-      hvec[0].len = b > rawmpilen? b - rawmpilen : 0;
-      hvec[1].data = rawmpi;
-      hvec[1].off = 0;
-      hvec[1].len = rawmpilen;
-      /* FIXME: Put and take the hash algo from the context.  */
-      rc = _gcry_md_hash_buffers (GCRY_MD_SHA512, 0, digest, hvec, 2);
-      gcry_free (rawmpi);
-      if (rc)
-        {
-          gcry_free (digest);
-          return NULL;
-        }
-
-      /* Compute the A value.  */
-      reverse_buffer (digest, 32);  /* Only the first half of the hash.  */
-      digest[0] = (digest[0] & 0x7f) | 0x40;
-      digest[31] &= 0xf8;
       a = mpi_snew (0);
       _gcry_mpi_set_buffer (a, digest, 32, 0);
-      gcry_free (digest);
+      xfree (digest);
 
       /* And finally the public key.  */
       if (!Q)
@@ -331,3 +287,83 @@ _gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec,
 
   return Q;
 }
+
+
+gpg_err_code_t
+_gcry_ecc_mont_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result)
+{
+  unsigned char *rawmpi;
+  unsigned int rawmpilen;
+
+  if (mpi_is_opaque (pk))
+    {
+      const unsigned char *buf;
+      unsigned char *p;
+
+      buf = mpi_get_opaque (pk, &rawmpilen);
+      if (!buf)
+        return GPG_ERR_INV_OBJ;
+      rawmpilen = (rawmpilen + 7)/8;
+
+      if (rawmpilen > 1 && (rawmpilen%2) && buf[0] == 0x40)
+        {
+          rawmpilen--;
+          buf++;
+        }
+
+      rawmpi = xtrymalloc (rawmpilen? rawmpilen:1);
+      if (!rawmpi)
+        return gpg_err_code_from_syserror ();
+
+      p = rawmpi + rawmpilen;
+      while (p > rawmpi)
+        *--p = *buf++;
+    }
+  else
+    {
+      unsigned int nbytes = (ctx->nbits+7)/8;
+
+      rawmpi = _gcry_mpi_get_buffer (pk, nbytes, &rawmpilen, NULL);
+      if (!rawmpi)
+        return gpg_err_code_from_syserror ();
+      /*
+       * It is not reliable to assume that 0x40 means the prefix.
+       *
+       * For newer implementation, it is reliable since we always put
+       * 0x40 for x-only coordinate.
+       *
+       * For data with older implementation (non-released development
+       * version), it is possible to have the 0x40 as a part of data.
+       * Besides, when data was parsed as MPI, we might have 0x00
+       * prefix.
+       *
+       * So, we need to check if it's really the prefix or not.
+       * Only when it's the prefix, we remove it.
+       */
+      if (pk->nlimbs * BYTES_PER_MPI_LIMB < nbytes)
+        {/*
+          * It is possible for data created by older implementation
+          * to have shorter length when it was parsed as MPI.
+          */
+          unsigned int len = pk->nlimbs * BYTES_PER_MPI_LIMB;
+
+          memmove (rawmpi + nbytes - len, rawmpi, len);
+          memset (rawmpi, 0, nbytes - len);
+        }
+
+      /*
+       * When we have the prefix (0x40 or 0x00), it comes at the end,
+       * since it is taken by _gcry_mpi_get_buffer with little endian.
+       * Just setting RAWMPILEN to NBYTES is enough in this case.
+       * Othewise, RAWMPILEN is NBYTES already.
+       */
+      rawmpilen = nbytes;
+    }
+
+  rawmpi[0] &= (1 << (ctx->nbits % 8)) - 1;
+  _gcry_mpi_set_buffer (result->x, rawmpi, rawmpilen, 0);
+  xfree (rawmpi);
+  mpi_set_ui (result->z, 1);
+
+  return 0;
+}