mpi/ec: fix when 'unsigned long' is 32-bit but limb size is 64-bit
[libgcrypt.git] / cipher / poly1305.c
index 472ae42..22255fb 100644 (file)
@@ -38,7 +38,82 @@ static const char *selftest (void);
 \f
 
 
-#ifdef HAVE_U64_TYPEDEF
+#ifdef POLY1305_USE_SSE2
+
+void _gcry_poly1305_amd64_sse2_init_ext(void *state, const poly1305_key_t *key)
+                                       OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_amd64_sse2_finish_ext(void *state, const byte *m,
+                                                 size_t remaining,
+                                                 byte mac[16]) OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_amd64_sse2_blocks(void *ctx, const byte *m,
+                                             size_t bytes) OPS_FUNC_ABI;
+
+static const poly1305_ops_t poly1305_amd64_sse2_ops = {
+  POLY1305_SSE2_BLOCKSIZE,
+  _gcry_poly1305_amd64_sse2_init_ext,
+  _gcry_poly1305_amd64_sse2_blocks,
+  _gcry_poly1305_amd64_sse2_finish_ext
+};
+
+#else  /* !POLY1305_USE_SSE2 */
+
+static OPS_FUNC_ABI void poly1305_init_ext_ref32
+/**/                (void *state, const poly1305_key_t *key);
+static OPS_FUNC_ABI unsigned int poly1305_blocks_ref32
+/**/                (void *state, const byte *m, size_t bytes);
+static OPS_FUNC_ABI unsigned int poly1305_finish_ext_ref32
+/**/                (void *state, const byte * m,
+                     size_t remaining, byte mac[POLY1305_TAGLEN]);
+
+static const poly1305_ops_t poly1305_default_ops = {
+  POLY1305_REF_BLOCKSIZE,
+  poly1305_init_ext_ref32,
+  poly1305_blocks_ref32,
+  poly1305_finish_ext_ref32
+};
+
+#endif /* !POLY1305_USE_SSE2 */
+
+
+#ifdef POLY1305_USE_AVX2
+
+void _gcry_poly1305_amd64_avx2_init_ext(void *state, const poly1305_key_t *key)
+                                       OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_amd64_avx2_finish_ext(void *state, const byte *m,
+                                                 size_t remaining,
+                                                 byte mac[16]) OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_amd64_avx2_blocks(void *ctx, const byte *m,
+                                             size_t bytes) OPS_FUNC_ABI;
+
+static const poly1305_ops_t poly1305_amd64_avx2_ops = {
+  POLY1305_AVX2_BLOCKSIZE,
+  _gcry_poly1305_amd64_avx2_init_ext,
+  _gcry_poly1305_amd64_avx2_blocks,
+  _gcry_poly1305_amd64_avx2_finish_ext
+};
+
+#endif
+
+
+#ifdef POLY1305_USE_NEON
+
+void _gcry_poly1305_armv7_neon_init_ext(void *state, const poly1305_key_t *key)
+                                       OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_armv7_neon_finish_ext(void *state, const byte *m,
+                                                 size_t remaining,
+                                                 byte mac[16]) OPS_FUNC_ABI;
+unsigned int _gcry_poly1305_armv7_neon_blocks(void *ctx, const byte *m,
+                                             size_t bytes) OPS_FUNC_ABI;
+
+static const poly1305_ops_t poly1305_armv7_neon_ops = {
+  POLY1305_NEON_BLOCKSIZE,
+  _gcry_poly1305_armv7_neon_init_ext,
+  _gcry_poly1305_armv7_neon_blocks,
+  _gcry_poly1305_armv7_neon_finish_ext
+};
+
+#endif
+
 
 /* Reference unoptimized poly1305 implementation using 32 bit * 32 bit = 64 bit
  * multiplication and 64 bit addition.
@@ -53,7 +128,8 @@ typedef struct poly1305_state_ref32_s
 } poly1305_state_ref32_t;
 
 
-static void
+#ifndef POLY1305_USE_SSE2
+static OPS_FUNC_ABI void
 poly1305_init_ext_ref32 (void *state, const poly1305_key_t * key)
 {
   poly1305_state_ref32_t *st = (poly1305_state_ref32_t *) state;
@@ -83,9 +159,11 @@ poly1305_init_ext_ref32 (void *state, const poly1305_key_t * key)
 
   st->final = 0;
 }
+#endif /* !POLY1305_USE_SSE2 */
 
 
-static unsigned int
+#ifndef POLY1305_USE_SSE2
+static OPS_FUNC_ABI unsigned int
 poly1305_blocks_ref32 (void *state, const byte * m, size_t bytes)
 {
   poly1305_state_ref32_t *st = (poly1305_state_ref32_t *) state;
@@ -171,9 +249,11 @@ poly1305_blocks_ref32 (void *state, const byte * m, size_t bytes)
 
   return (16 * sizeof (u32) + 5 * sizeof (u64) + 5 * sizeof (void *));
 }
+#endif /* !POLY1305_USE_SSE2 */
 
 
-static unsigned int
+#ifndef POLY1305_USE_SSE2
+static OPS_FUNC_ABI unsigned int
 poly1305_finish_ext_ref32 (void *state, const byte * m,
                           size_t remaining, byte mac[POLY1305_TAGLEN])
 {
@@ -289,227 +369,9 @@ poly1305_finish_ext_ref32 (void *state, const byte * m,
   return (13 * sizeof (u32) + sizeof (u64) +
          POLY1305_REF_BLOCKSIZE + 6 * sizeof (void *)) + burn;
 }
+#endif /* !POLY1305_USE_SSE2*/
 
 
-static const poly1305_ops_t poly1305_default_ops = {
-  POLY1305_REF_BLOCKSIZE,
-  poly1305_init_ext_ref32,
-  poly1305_blocks_ref32,
-  poly1305_finish_ext_ref32
-};
-
-#else /* !HAVE_U64_TYPEDEF */
-
-/* Reference unoptimized poly1305 implementation using 8 bit * 8 bit = 16 bit
- * multiplication and 16 bit addition, used when we don't have 'u64'.
- */
-
-typedef struct poly1305_state_ref8_t
-{
-  byte h[17];
-  byte r[17];
-  byte pad[17];
-  byte final;
-} poly1305_state_ref8_t;
-
-
-static void
-poly1305_init_ext_ref8 (void *state, const poly1305_key_t * key)
-{
-  poly1305_state_ref8_t *st = (poly1305_state_ref8_t *) state;
-  size_t i;
-
-  /* h = 0 */
-  for (i = 0; i < 17; i++)
-    st->h[i] = 0;
-
-  /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
-  st->r[0] = key->b[0];
-  st->r[1] = key->b[1];
-  st->r[2] = key->b[2];
-  st->r[3] = key->b[3] & 0x0f;
-  st->r[4] = key->b[4] & 0xfc;
-  st->r[5] = key->b[5];
-  st->r[6] = key->b[6];
-  st->r[7] = key->b[7] & 0x0f;
-  st->r[8] = key->b[8] & 0xfc;
-  st->r[9] = key->b[9];
-  st->r[10] = key->b[10];
-  st->r[11] = key->b[11] & 0x0f;
-  st->r[12] = key->b[12] & 0xfc;
-  st->r[13] = key->b[13];
-  st->r[14] = key->b[14];
-  st->r[15] = key->b[15] & 0x0f;
-  st->r[16] = 0;
-
-  /* save pad for later */
-  for (i = 0; i < 16; i++)
-    st->pad[i] = key->b[i + 16];
-  st->pad[16] = 0;
-
-  st->final = 0;
-}
-
-
-static void
-poly1305_add_ref8 (byte h[17], const byte c[17])
-{
-  u16 u;
-  unsigned int i;
-  for (u = 0, i = 0; i < 17; i++)
-    {
-      u += (u16) h[i] + (u16) c[i];
-      h[i] = (byte) u & 0xff;
-      u >>= 8;
-    }
-}
-
-
-static void
-poly1305_squeeze_ref8 (byte h[17], u32 hr[17])
-{
-  u32 u;
-  unsigned int i;
-  u = 0;
-  for (i = 0; i < 16; i++)
-    {
-      u += hr[i];
-      h[i] = (byte) u & 0xff;
-      u >>= 8;
-    }
-  u += hr[16];
-  h[16] = (byte) u & 0x03;
-  u >>= 2;
-  u += (u << 2);               /* u *= 5; */
-  for (i = 0; i < 16; i++)
-    {
-      u += h[i];
-      h[i] = (byte) u & 0xff;
-      u >>= 8;
-    }
-  h[16] += (byte) u;
-}
-
-
-static void
-poly1305_freeze_ref8 (byte h[17])
-{
-  static const byte minusp[17] = {
-    0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-    0xfc
-  };
-  byte horig[17], negative;
-  unsigned int i;
-
-  /* compute h + -p */
-  for (i = 0; i < 17; i++)
-    horig[i] = h[i];
-  poly1305_add_ref8 (h, minusp);
-
-  /* select h if h < p, or h + -p if h >= p */
-  negative = -(h[16] >> 7);
-  for (i = 0; i < 17; i++)
-    h[i] ^= negative & (horig[i] ^ h[i]);
-}
-
-
-static unsigned int
-poly1305_blocks_ref8 (void *state, const byte * m, size_t bytes)
-{
-  poly1305_state_ref8_t *st = (poly1305_state_ref8_t *) state;
-  const byte hibit = st->final ^ 1;    /* 1 << 128 */
-
-  while (bytes >= POLY1305_REF_BLOCKSIZE)
-    {
-      u32 hr[17], u;
-      byte c[17];
-      unsigned int i, j;
-
-      /* h += m */
-      for (i = 0; i < 16; i++)
-       c[i] = m[i];
-      c[16] = hibit;
-      poly1305_add_ref8 (st->h, c);
-
-      /* h *= r */
-      for (i = 0; i < 17; i++)
-       {
-         u = 0;
-         for (j = 0; j <= i; j++)
-           {
-             u += (u16) st->h[j] * st->r[i - j];
-           }
-         for (j = i + 1; j < 17; j++)
-           {
-             u32 v = (u16) st->h[j] * st->r[i + 17 - j];
-             v = ((v << 8) + (v << 6));        /* v *= (5 << 6); */
-             u += v;
-           }
-         hr[i] = u;
-       }
-
-      /* (partial) h %= p */
-      poly1305_squeeze_ref8 (st->h, hr);
-
-      m += POLY1305_REF_BLOCKSIZE;
-      bytes -= POLY1305_REF_BLOCKSIZE;
-    }
-
-  /* burn_stack */
-  return (18 + 2) * sizeof (u32) + 18 + 6 * sizeof (void *) +
-    6 * sizeof (void *);
-}
-
-
-static unsigned int
-poly1305_finish_ext_ref8 (void *state, const byte * m, size_t remaining,
-                         byte mac[POLY1305_TAGLEN])
-{
-  poly1305_state_ref8_t *st = (poly1305_state_ref8_t *) state;
-  size_t i;
-  unsigned int burn = 0;
-
-  /* process the remaining block */
-  if (remaining)
-    {
-      byte final[POLY1305_REF_BLOCKSIZE] = { 0 };
-      for (i = 0; i < remaining; i++)
-       final[i] = m[i];
-      final[remaining] = 1;
-      st->final = 1;
-      burn = poly1305_blocks_ref8 (st, final, POLY1305_REF_BLOCKSIZE);
-    }
-
-  /* fully reduce h */
-  poly1305_freeze_ref8 (st->h);
-
-  /* h = (h + pad) % (1 << 128) */
-  poly1305_add_ref8 (st->h, st->pad);
-  for (i = 0; i < 16; i++)
-    mac[i] = st->h[i];
-
-  /* zero out the state */
-  for (i = 0; i < 17; i++)
-    st->h[i] = 0;
-  for (i = 0; i < 17; i++)
-    st->r[i] = 0;
-  for (i = 0; i < 17; i++)
-    st->pad[i] = 0;
-
-  /* burn_stack */
-  return POLY1305_REF_BLOCKSIZE + 18 + 16 * sizeof (void *) + burn;
-}
-
-
-static const poly1305_ops_t poly1305_default_ops = {
-  POLY1305_REF_BLOCKSIZE,
-  poly1305_init_ext_ref8,
-  poly1305_blocks_ref8,
-  poly1305_finish_ext_ref8
-};
-
-#endif /* HAVE_U64_TYPEDEF */
 \f
 
 
@@ -597,6 +459,7 @@ _gcry_poly1305_init (poly1305_context_t * ctx, const byte * key,
   static int initialized;
   static const char *selftest_failed;
   poly1305_key_t keytmp;
+  unsigned int features = _gcry_get_hw_features ();
 
   if (!initialized)
     {
@@ -612,7 +475,21 @@ _gcry_poly1305_init (poly1305_context_t * ctx, const byte * key,
   if (selftest_failed)
     return GPG_ERR_SELFTEST_FAILED;
 
+#ifdef POLY1305_USE_SSE2
+  ctx->ops = &poly1305_amd64_sse2_ops;
+#else
   ctx->ops = &poly1305_default_ops;
+#endif
+
+#ifdef POLY1305_USE_AVX2
+  if (features & HWF_INTEL_AVX2)
+    ctx->ops = &poly1305_amd64_avx2_ops;
+#endif
+#ifdef POLY1305_USE_NEON
+  if (features & HWF_ARM_NEON)
+    ctx->ops = &poly1305_armv7_neon_ops;
+#endif
+  (void)features;
 
   buf_cpy (keytmp.b, key, POLY1305_KEYLEN);
   poly1305_init (ctx, &keytmp);