mpi/ec: fix when 'unsigned long' is 32-bit but limb size is 64-bit
[libgcrypt.git] / cipher / ecc-misc.c
index 67e3b3d..41debe4 100644 (file)
@@ -292,7 +292,6 @@ _gcry_ecc_compute_public (mpi_point_t Q, mpi_ec_t ec,
 gpg_err_code_t
 _gcry_ecc_mont_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result)
 {
-  unsigned char *a;
   unsigned char *rawmpi;
   unsigned int rawmpilen;
 
@@ -312,8 +311,8 @@ _gcry_ecc_mont_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result)
           buf++;
         }
 
-      a = rawmpi = xtrymalloc (rawmpilen? rawmpilen:1);
-      if (!a)
+      rawmpi = xtrymalloc (rawmpilen? rawmpilen:1);
+      if (!rawmpi)
         return gpg_err_code_from_syserror ();
 
       p = rawmpi + rawmpilen;
@@ -322,8 +321,10 @@ _gcry_ecc_mont_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result)
     }
   else
     {
-      a = rawmpi = _gcry_mpi_get_buffer (pk, ctx->nbits/8, &rawmpilen, NULL);
-      if (!a)
+      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.
@@ -332,37 +333,36 @@ _gcry_ecc_mont_decodepoint (gcry_mpi_t pk, mpi_ec_t ctx, mpi_point_t result)
        * 0x40 for x-only coordinate.
        *
        * For data with older implementation (non-released development
-       * version), it is possibe to have the 0x40 as a part of data.
+       * 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 (ctx->nbits/8 == rawmpilen - 1)
-        rawmpi++;
-      else if (rawmpilen < ctx->nbits/8)
+      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 new_rawmpilen = ctx->nbits/8;
-
-          rawmpi = xtrymalloc (new_rawmpilen);
-          if (!rawmpi)
-            {
-              gpg_err_code_t err = gpg_err_code_from_syserror ();
-              xfree (a);
-              return err;
-            }
-
-          memset (rawmpi, 0, new_rawmpilen - rawmpilen);
-          memcpy (rawmpi + new_rawmpilen - rawmpilen, a, rawmpilen);
+          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 (a);
+  xfree (rawmpi);
   mpi_set_ui (result->z, 1);
 
   return 0;