mpi/ec: fix when 'unsigned long' is 32-bit but limb size is 64-bit
[libgcrypt.git] / cipher / ecc-misc.c
index 5c86121..41debe4 100644 (file)
@@ -26,6 +26,7 @@
 
 #include "g10lib.h"
 #include "mpi.h"
+#include "cipher.h"
 #include "context.h"
 #include "ec-context.h"
 #include "ecc-common.h"
@@ -42,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;
 }
 
 
@@ -53,12 +55,16 @@ _gcry_ecc_curve_copy (elliptic_curve_t E)
 {
   elliptic_curve_t R;
 
+  R.model = E.model;
+  R.dialect = E.dialect;
+  R.name = E.name;
   R.p = mpi_copy (E.p);
   R.a = mpi_copy (E.a);
   R.b = mpi_copy (E.b);
   _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;
 }
@@ -75,48 +81,62 @@ _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;
 }
 
 
+/*
+ * Return a description of the curve dialect.
+ */
+const char *
+_gcry_ecc_dialect2str (enum ecc_dialects dialect)
+{
+  const char *str = "?";
+  switch (dialect)
+    {
+    case ECC_DIALECT_STANDARD:  str = "Standard"; break;
+    case ECC_DIALECT_ED25519:   str = "Ed25519"; break;
+    }
+  return str;
+}
 
 
 gcry_mpi_t
 _gcry_ecc_ec2os (gcry_mpi_t x, gcry_mpi_t y, gcry_mpi_t p)
 {
-  gpg_error_t err;
+  gpg_err_code_t rc;
   int pbytes = (mpi_get_nbits (p)+7)/8;
   size_t n;
   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;
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
-  if (err)
-    log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
+  rc = _gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, x);
+  if (rc)
+    log_fatal ("mpi_print failed: %s\n", gpg_strerror (rc));
   if (n < pbytes)
     {
       memmove (ptr+(pbytes-n), ptr, n);
       memset (ptr, 0, (pbytes-n));
     }
   ptr += pbytes;
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y);
-  if (err)
-    log_fatal ("mpi_print failed: %s\n", gpg_strerror (err));
+  rc = _gcry_mpi_print (GCRYMPI_FMT_USG, ptr, pbytes, &n, y);
+  if (rc)
+    log_fatal ("mpi_print failed: %s\n", gpg_strerror (rc));
   if (n < pbytes)
     {
       memmove (ptr+(pbytes-n), ptr, n);
       memset (ptr, 0, (pbytes-n));
     }
 
-  err = gcry_mpi_scan (&result, GCRYMPI_FMT_USG, buf, 1+2*pbytes, NULL);
-  if (err)
-    log_fatal ("mpi_scan failed: %s\n", gpg_strerror (err));
-  gcry_free (buf);
+  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));
+  xfree (buf);
 
   return result;
 }
@@ -145,50 +165,66 @@ _gcry_mpi_ec_ec2os (gcry_mpi_point_t point, mpi_ec_t ectx)
 
 /* RESULT must have been initialized and is set on success to the
    point given by VALUE.  */
-gcry_error_t
+gcry_err_code_t
 _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value)
 {
-  gcry_error_t err;
+  gcry_err_code_t rc;
   size_t n;
-  unsigned char *buf;
+  const unsigned char *buf;
+  unsigned char *buf_memory;
   gcry_mpi_t x, y;
 
-  n = (mpi_get_nbits (value)+7)/8;
-  buf = gcry_xmalloc (n);
-  err = gcry_mpi_print (GCRYMPI_FMT_USG, buf, n, &n, value);
-  if (err)
+  if (mpi_is_opaque (value))
+    {
+      unsigned int nbits;
+
+      buf = mpi_get_opaque (value, &nbits);
+      if (!buf)
+        return GPG_ERR_INV_OBJ;
+      n = (nbits + 7)/8;
+      buf_memory = NULL;
+    }
+  else
     {
-      gcry_free (buf);
-      return err;
+      n = (mpi_get_nbits (value)+7)/8;
+      buf_memory = xmalloc (n);
+      rc = _gcry_mpi_print (GCRYMPI_FMT_USG, buf_memory, n, &n, value);
+      if (rc)
+        {
+          xfree (buf_memory);
+          return rc;
+        }
+      buf = buf_memory;
     }
+
   if (n < 1)
     {
-      gcry_free (buf);
+      xfree (buf_memory);
       return GPG_ERR_INV_OBJ;
     }
   if (*buf != 4)
     {
-      gcry_free (buf);
+      xfree (buf_memory);
       return GPG_ERR_NOT_IMPLEMENTED; /* No support for point compression.  */
     }
   if ( ((n-1)%2) )
     {
-      gcry_free (buf);
+      xfree (buf_memory);
       return GPG_ERR_INV_OBJ;
     }
   n = (n-1)/2;
-  err = gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
-  if (err)
+  rc = _gcry_mpi_scan (&x, GCRYMPI_FMT_USG, buf+1, n, NULL);
+  if (rc)
     {
-      gcry_free (buf);
-      return err;
+      xfree (buf_memory);
+      return rc;
     }
-  err = gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
-  gcry_free (buf);
-  if (err)
+  rc = _gcry_mpi_scan (&y, GCRYMPI_FMT_USG, buf+1+n, n, NULL);
+  xfree (buf_memory);
+  if (rc)
     {
       mpi_free (x);
-      return err;
+      return rc;
     }
 
   mpi_set (result->x, x);
@@ -200,3 +236,134 @@ _gcry_ecc_os2ec (mpi_point_t result, gcry_mpi_t value)
 
   return 0;
 }
+
+
+/* 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
+   is returned.  If G or D are given they override the values taken
+   from EC. */
+mpi_point_t
+_gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec,
+                          mpi_point_t G, gcry_mpi_t d)
+{
+  if (!G)
+    G = ec->G;
+  if (!d)
+    d = ec->d;
+
+  if (!d || !G || !ec->p || !ec->a)
+    return NULL;
+  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 *digest;
+
+      if (_gcry_ecc_eddsa_compute_h_d (&digest, d, ec))
+        return NULL;
+
+      a = mpi_snew (0);
+      _gcry_mpi_set_buffer (a, digest, 32, 0);
+      xfree (digest);
+
+      /* And finally the public key.  */
+      if (!Q)
+        Q = mpi_point_new (0);
+      if (Q)
+        _gcry_mpi_ec_mul_point (Q, a, G, ec);
+      mpi_free (a);
+    }
+  else
+    {
+      if (!Q)
+        Q = mpi_point_new (0);
+      if (Q)
+        _gcry_mpi_ec_mul_point (Q, d, G, 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;
+}