[PATCH 1/3] Add Poly1305 MAC

Jussi Kivilinna jussi.kivilinna at iki.fi
Sat May 10 23:56:33 CEST 2014


* cipher/Makefile.am: Add 'mac-poly1305.c', 'poly1305.c' and
'poly1305-internal.h'.
* cipher/mac-internal.h (poly1305mac_context_s): New.
(gcry_mac_handle): Add 'u.poly1305mac'.
(_gcry_mac_type_spec_poly1305mac): New.
* cipher/mac-poly1305.c: New.
* cipher/mac.c (mac_list): Add Poly1305.
* cipher/poly1305-internal.h: New.
* cipher/poly1305.c: New.
* src/gcrypt.h.in: Add 'GCRY_MAC_POLY1305'.
* tests/basic.c (check_mac): Add Poly1035 test vectors; Allow
overriding lengths of data and key buffers.
* tests/bench-slope.c (mac_bench): Increase max algo number from 500 to
600.
* tests/benchmark.c (mac_bench): Ditto.
--

Patch adds Bernstein's Poly1305 message authentication code to libgcrypt.
Implementation is based on Andrew Moon's public domain implementation
from: https://github.com/floodyberry/poly1305-opt

The algorithm added by this patch is the plain Poly1305 without AES and
takes 32-bit key that must not be reused.

Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
 cipher/Makefile.am         |    3 
 cipher/mac-internal.h      |   17 +
 cipher/mac-poly1305.c      |  213 ++++++++++++
 cipher/mac.c               |    1 
 cipher/poly1305-internal.h |   90 +++++
 cipher/poly1305.c          |  780 ++++++++++++++++++++++++++++++++++++++++++++
 src/gcrypt.h.in            |    4 
 tests/basic.c              |  120 ++++++-
 tests/bench-slope.c        |    2 
 tests/benchmark.c          |    2 
 10 files changed, 1221 insertions(+), 11 deletions(-)
 create mode 100644 cipher/mac-poly1305.c
 create mode 100644 cipher/poly1305-internal.h
 create mode 100644 cipher/poly1305.c

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index 26d13d2..a8b86e6 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -45,7 +45,8 @@ cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
 mac.c mac-internal.h \
-mac-hmac.c mac-cmac.c mac-gmac.c \
+mac-hmac.c mac-cmac.c mac-gmac.c mac-poly1305.c \
+poly1305.c poly1305-internal.h \
 kdf.c kdf-internal.h \
 hmac-tests.c \
 bithelp.h  \
diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h
index 9895a54..81b6185 100644
--- a/cipher/mac-internal.h
+++ b/cipher/mac-internal.h
@@ -17,9 +17,17 @@
  * License along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
+#include <config.h>
+
+#include "g10lib.h"
+
+
 /* The data object used to hold a handle to an encryption object.  */
 struct gcry_mac_handle;
 
+/* The data object used to hold poly1305-mac context.  */
+struct poly1305mac_context_s;
+
 

 /*
  *
@@ -84,7 +92,6 @@ typedef struct gcry_mac_spec
 } gcry_mac_spec_t;
 
 
-
 /* The handle structure.  */
 struct gcry_mac_handle
 {
@@ -106,6 +113,9 @@ struct gcry_mac_handle
       gcry_cipher_hd_t ctx;
       int cipher_algo;
     } gmac;
+    struct {
+      struct poly1305mac_context_s *ctx;
+    } poly1305mac;
   } u;
 };
 
@@ -202,3 +212,8 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_gmac_seed;
 #if USE_CAMELLIA
 extern gcry_mac_spec_t _gcry_mac_type_spec_gmac_camellia;
 #endif
+
+/*
+ * The Poly1305 MAC algorithm specifications (mac-poly1305.c).
+ */
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac;
diff --git a/cipher/mac-poly1305.c b/cipher/mac-poly1305.c
new file mode 100644
index 0000000..e265b64
--- /dev/null
+++ b/cipher/mac-poly1305.c
@@ -0,0 +1,213 @@
+/* mac-poly1305.c  -  Poly1305 based MACs
+ * Copyright (C) 2014 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "mac-internal.h"
+#include "poly1305-internal.h"
+
+
+struct poly1305mac_context_s {
+  poly1305_context_t ctx;
+  struct {
+    unsigned int key_set:1;
+    unsigned int tag:1;
+  } marks;
+  byte tag[POLY1305_TAGLEN];
+  byte key[POLY1305_KEYLEN];
+};
+
+
+static gcry_err_code_t
+poly1305mac_open (gcry_mac_hd_t h)
+{
+  struct poly1305mac_context_s *mac_ctx;
+  int secure = (h->magic == CTX_MAGIC_SECURE);
+
+  if (secure)
+    mac_ctx = xtrycalloc_secure (1, sizeof(*mac_ctx));
+  else
+    mac_ctx = xtrycalloc (1, sizeof(*mac_ctx));
+
+  if (!mac_ctx)
+    return gpg_err_code_from_syserror ();
+
+  h->u.poly1305mac.ctx = mac_ctx;
+
+  return 0;
+}
+
+
+static void
+poly1305mac_close (gcry_mac_hd_t h)
+{
+  xfree(h->u.poly1305mac.ctx);
+}
+
+
+static gcry_err_code_t
+poly1305mac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  gcry_err_code_t err;
+
+  memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
+  memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
+  memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
+
+  mac_ctx->marks.key_set = 0;
+  mac_ctx->marks.tag = 0;
+
+  if (keylen != POLY1305_KEYLEN)
+    return GPG_ERR_INV_KEYLEN;
+
+  memcpy(mac_ctx->key, key, POLY1305_KEYLEN);
+
+  err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
+  if (err)
+    {
+      memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
+      return err;
+    }
+
+  mac_ctx->marks.key_set = 1;
+
+  return 0;
+}
+
+
+static gcry_err_code_t
+poly1305mac_reset (gcry_mac_hd_t h)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+
+  if (!mac_ctx->marks.key_set)
+    return GPG_ERR_INV_STATE;
+
+  memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
+  memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
+
+  mac_ctx->marks.key_set = 1;
+  mac_ctx->marks.tag = 0;
+
+  return _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
+}
+
+
+static gcry_err_code_t
+poly1305mac_write (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+
+  if (!mac_ctx->marks.key_set || mac_ctx->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  _gcry_poly1305_update (&mac_ctx->ctx, buf, buflen);
+  return 0;
+}
+
+
+static gcry_err_code_t
+poly1305mac_read (gcry_mac_hd_t h, unsigned char *outbuf, size_t *outlen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+
+  if (!mac_ctx->marks.key_set)
+    return GPG_ERR_INV_STATE;
+
+  if (!mac_ctx->marks.tag)
+    {
+      _gcry_poly1305_finish(&mac_ctx->ctx, mac_ctx->tag);
+
+      memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
+      mac_ctx->marks.tag = 1;
+    }
+
+  if (*outlen <= POLY1305_TAGLEN)
+    buf_cpy (outbuf, mac_ctx->tag, *outlen);
+  else
+    {
+      buf_cpy (outbuf, mac_ctx->tag, POLY1305_TAGLEN);
+      *outlen = POLY1305_TAGLEN;
+    }
+
+  return 0;
+}
+
+
+static gcry_err_code_t
+poly1305mac_verify (gcry_mac_hd_t h, const unsigned char *buf, size_t buflen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  gcry_err_code_t err;
+  size_t outlen = 0;
+
+  /* Check and finalize tag. */
+  err = poly1305mac_read(h, NULL, &outlen);
+  if (err)
+    return err;
+
+  if (buflen > POLY1305_TAGLEN)
+    return GPG_ERR_INV_LENGTH;
+
+  return buf_eq_const (buf, mac_ctx->tag, buflen) ? 0 : GPG_ERR_CHECKSUM;
+}
+
+
+static unsigned int
+poly1305mac_get_maclen (int algo)
+{
+  (void)algo;
+
+  return POLY1305_TAGLEN;
+}
+
+
+static unsigned int
+poly1305mac_get_keylen (int algo)
+{
+  (void)algo;
+
+  return POLY1305_KEYLEN;
+}
+
+
+static gcry_mac_spec_ops_t poly1305mac_ops = {
+  poly1305mac_open,
+  poly1305mac_close,
+  poly1305mac_setkey,
+  NULL,
+  poly1305mac_reset,
+  poly1305mac_write,
+  poly1305mac_read,
+  poly1305mac_verify,
+  poly1305mac_get_maclen,
+  poly1305mac_get_keylen
+};
+
+
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac = {
+  GCRY_MAC_POLY1305, {0, 0}, "POLY1305",
+  &poly1305mac_ops
+};
diff --git a/cipher/mac.c b/cipher/mac.c
index 7805467..e583369 100644
--- a/cipher/mac.c
+++ b/cipher/mac.c
@@ -101,6 +101,7 @@ static gcry_mac_spec_t *mac_list[] = {
 #if USE_GOST28147
   &_gcry_mac_type_spec_cmac_gost28147,
 #endif
+  &_gcry_mac_type_spec_poly1305mac,
   NULL,
 };
 
diff --git a/cipher/poly1305-internal.h b/cipher/poly1305-internal.h
new file mode 100644
index 0000000..d89db16
--- /dev/null
+++ b/cipher/poly1305-internal.h
@@ -0,0 +1,90 @@
+/* poly1305-internal.h  -  Poly1305 internals
+ * Copyright (C) 2014 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef G10_POLY1305_INTERNAL_H
+#define G10_POLY1305_INTERNAL_H
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "types.h"
+#include "g10lib.h"
+#include "cipher.h"
+#include "bufhelp.h"
+
+
+#define POLY1305_TAGLEN 16
+#define POLY1305_KEYLEN 32
+
+
+/* Block-size used in default implementation. */
+#define POLY1305_REF_BLOCKSIZE 16
+
+/* State size of default implementation. */
+#define POLY1305_REF_STATESIZE 64
+
+
+/* Largest block-size used in any implementation (optimized implementations
+ * might use block-size multiple of 16). */
+#define POLY1305_LARGEST_BLOCKSIZE POLY1305_REF_BLOCKSIZE
+
+/* Largest state-size used in any implementation. */
+#define POLY1305_LARGEST_STATESIZE POLY1305_REF_STATESIZE
+
+/* Minimum alignment for state pointer passed to implementations. */
+#define POLY1305_STATE_ALIGNMENT 32
+
+
+typedef struct poly1305_key_s
+{
+  byte b[POLY1305_KEYLEN];
+} poly1305_key_t;
+
+
+typedef struct poly1305_ops_s
+{
+  unsigned int (*block_size) (void);
+  void (*init_ext) (void *ctx, const poly1305_key_t * key);
+  unsigned int (*blocks) (void *ctx, const byte * m, size_t bytes);
+  unsigned int (*finish_ext) (void *ctx, const byte * m, size_t remaining,
+			      byte mac[POLY1305_TAGLEN]);
+} poly1305_ops_t;
+
+
+typedef struct poly1305_context_s
+{
+  byte state[POLY1305_LARGEST_STATESIZE + POLY1305_STATE_ALIGNMENT];
+  const poly1305_ops_t *ops;
+  unsigned int leftover, block_size;
+  byte buffer[POLY1305_LARGEST_BLOCKSIZE];
+} poly1305_context_t;
+
+
+gcry_err_code_t _gcry_poly1305_init (poly1305_context_t * ctx, const byte * key,
+				     size_t keylen);
+
+void _gcry_poly1305_finish (poly1305_context_t * ctx,
+			    byte mac[POLY1305_TAGLEN]);
+
+void _gcry_poly1305_update (poly1305_context_t * ctx, const byte * buf,
+			    size_t buflen);
+
+
+#endif /* G10_POLY1305_INTERNAL_H */
diff --git a/cipher/poly1305.c b/cipher/poly1305.c
new file mode 100644
index 0000000..00ffea8
--- /dev/null
+++ b/cipher/poly1305.c
@@ -0,0 +1,780 @@
+/* poly1305.c  -  Poly1305 internals and generic implementation
+ * Copyright (C) 2014 Jussi Kivilinna <jussi.kivilinna at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* The code is based on public-domain Poly1305 implementation by
+ * Andrew Moon at
+ *  https://github.com/floodyberry/poly1305-opt
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "types.h"
+#include "g10lib.h"
+#include "cipher.h"
+#include "bufhelp.h"
+#include "poly1305-internal.h"
+
+
+static const char *selftest (void);
+

+
+
+#ifdef HAVE_U64_TYPEDEF
+
+/* Reference unoptimized poly1305 implementation using 32 bit * 32 bit = 64 bit
+ * multiplication and 64 bit addition.
+ */
+
+typedef struct poly1305_state_ref32_s
+{
+  u32 r[5];
+  u32 h[5];
+  u32 pad[4];
+  byte final;
+} poly1305_state_ref32_t;
+
+
+static unsigned int
+poly1305_block_size_ref32 (void)
+{
+  return POLY1305_REF_BLOCKSIZE;
+}
+
+
+static void
+poly1305_init_ext_ref32 (void *state, const poly1305_key_t * key)
+{
+  poly1305_state_ref32_t *st = (poly1305_state_ref32_t *) state;
+
+  gcry_assert (sizeof (*st) + POLY1305_STATE_ALIGNMENT <=
+	       sizeof (((poly1305_context_t *) 0)->state));
+
+  /* r &= 0xffffffc0ffffffc0ffffffc0fffffff */
+  st->r[0] = (buf_get_le32 (&key->b[0])) & 0x3ffffff;
+  st->r[1] = (buf_get_le32 (&key->b[3]) >> 2) & 0x3ffff03;
+  st->r[2] = (buf_get_le32 (&key->b[6]) >> 4) & 0x3ffc0ff;
+  st->r[3] = (buf_get_le32 (&key->b[9]) >> 6) & 0x3f03fff;
+  st->r[4] = (buf_get_le32 (&key->b[12]) >> 8) & 0x00fffff;
+
+  /* h = 0 */
+  st->h[0] = 0;
+  st->h[1] = 0;
+  st->h[2] = 0;
+  st->h[3] = 0;
+  st->h[4] = 0;
+
+  /* save pad for later */
+  st->pad[0] = buf_get_le32 (&key->b[16]);
+  st->pad[1] = buf_get_le32 (&key->b[20]);
+  st->pad[2] = buf_get_le32 (&key->b[24]);
+  st->pad[3] = buf_get_le32 (&key->b[28]);
+
+  st->final = 0;
+}
+
+
+static unsigned int
+poly1305_blocks_ref32 (void *state, const byte * m, size_t bytes)
+{
+  poly1305_state_ref32_t *st = (poly1305_state_ref32_t *) state;
+  const u32 hibit = (st->final) ? 0 : (1 << 24);	/* 1 << 128 */
+  u32 r0, r1, r2, r3, r4;
+  u32 s1, s2, s3, s4;
+  u32 h0, h1, h2, h3, h4;
+  u64 d0, d1, d2, d3, d4;
+  u32 c;
+
+  r0 = st->r[0];
+  r1 = st->r[1];
+  r2 = st->r[2];
+  r3 = st->r[3];
+  r4 = st->r[4];
+
+  s1 = r1 * 5;
+  s2 = r2 * 5;
+  s3 = r3 * 5;
+  s4 = r4 * 5;
+
+  h0 = st->h[0];
+  h1 = st->h[1];
+  h2 = st->h[2];
+  h3 = st->h[3];
+  h4 = st->h[4];
+
+  while (bytes >= POLY1305_REF_BLOCKSIZE)
+    {
+      /* h += m[i] */
+      h0 += (buf_get_le32 (m + 0)) & 0x3ffffff;
+      h1 += (buf_get_le32 (m + 3) >> 2) & 0x3ffffff;
+      h2 += (buf_get_le32 (m + 6) >> 4) & 0x3ffffff;
+      h3 += (buf_get_le32 (m + 9) >> 6) & 0x3ffffff;
+      h4 += (buf_get_le32 (m + 12) >> 8) | hibit;
+
+      /* h *= r */
+      d0 =
+	((u64) h0 * r0) + ((u64) h1 * s4) +
+	((u64) h2 * s3) + ((u64) h3 * s2) + ((u64) h4 * s1);
+      d1 =
+	((u64) h0 * r1) + ((u64) h1 * r0) +
+	((u64) h2 * s4) + ((u64) h3 * s3) + ((u64) h4 * s2);
+      d2 =
+	((u64) h0 * r2) + ((u64) h1 * r1) +
+	((u64) h2 * r0) + ((u64) h3 * s4) + ((u64) h4 * s3);
+      d3 =
+	((u64) h0 * r3) + ((u64) h1 * r2) +
+	((u64) h2 * r1) + ((u64) h3 * r0) + ((u64) h4 * s4);
+      d4 =
+	((u64) h0 * r4) + ((u64) h1 * r3) +
+	((u64) h2 * r2) + ((u64) h3 * r1) + ((u64) h4 * r0);
+
+      /* (partial) h %= p */
+      c = (u32) (d0 >> 26);
+      h0 = (u32) d0 & 0x3ffffff;
+      d1 += c;
+      c = (u32) (d1 >> 26);
+      h1 = (u32) d1 & 0x3ffffff;
+      d2 += c;
+      c = (u32) (d2 >> 26);
+      h2 = (u32) d2 & 0x3ffffff;
+      d3 += c;
+      c = (u32) (d3 >> 26);
+      h3 = (u32) d3 & 0x3ffffff;
+      d4 += c;
+      c = (u32) (d4 >> 26);
+      h4 = (u32) d4 & 0x3ffffff;
+      h0 += c * 5;
+      c = (h0 >> 26);
+      h0 = h0 & 0x3ffffff;
+      h1 += c;
+
+      m += POLY1305_REF_BLOCKSIZE;
+      bytes -= POLY1305_REF_BLOCKSIZE;
+    }
+
+  st->h[0] = h0;
+  st->h[1] = h1;
+  st->h[2] = h2;
+  st->h[3] = h3;
+  st->h[4] = h4;
+
+  return (16 * sizeof (u32) + 5 * sizeof (u64) + 5 * sizeof (void *));
+}
+
+
+static unsigned int
+poly1305_finish_ext_ref32 (void *state, const byte * m,
+			   size_t remaining, byte mac[POLY1305_TAGLEN])
+{
+  poly1305_state_ref32_t *st = (poly1305_state_ref32_t *) state;
+  u32 h0, h1, h2, h3, h4, c;
+  u32 g0, g1, g2, g3, g4;
+  u64 f;
+  u32 mask;
+  unsigned int burn = 0;
+
+  /* process the remaining block */
+  if (remaining)
+    {
+      byte final[POLY1305_REF_BLOCKSIZE] = { 0 };
+      size_t i;
+      for (i = 0; i < remaining; i++)
+	final[i] = m[i];
+      final[remaining] = 1;
+      st->final = 1;
+      burn = poly1305_blocks_ref32 (st, final, POLY1305_REF_BLOCKSIZE);
+    }
+
+  /* fully carry h */
+  h0 = st->h[0];
+  h1 = st->h[1];
+  h2 = st->h[2];
+  h3 = st->h[3];
+  h4 = st->h[4];
+
+  c = h1 >> 26;
+  h1 = h1 & 0x3ffffff;
+  h2 += c;
+  c = h2 >> 26;
+  h2 = h2 & 0x3ffffff;
+  h3 += c;
+  c = h3 >> 26;
+  h3 = h3 & 0x3ffffff;
+  h4 += c;
+  c = h4 >> 26;
+  h4 = h4 & 0x3ffffff;
+  h0 += c * 5;
+  c = h0 >> 26;
+  h0 = h0 & 0x3ffffff;
+  h1 += c;
+
+  /* compute h + -p */
+  g0 = h0 + 5;
+  c = g0 >> 26;
+  g0 &= 0x3ffffff;
+  g1 = h1 + c;
+  c = g1 >> 26;
+  g1 &= 0x3ffffff;
+  g2 = h2 + c;
+  c = g2 >> 26;
+  g2 &= 0x3ffffff;
+  g3 = h3 + c;
+  c = g3 >> 26;
+  g3 &= 0x3ffffff;
+  g4 = h4 + c - (1 << 26);
+
+  /* select h if h < p, or h + -p if h >= p */
+  mask = (g4 >> ((sizeof (u32) * 8) - 1)) - 1;
+  g0 &= mask;
+  g1 &= mask;
+  g2 &= mask;
+  g3 &= mask;
+  g4 &= mask;
+  mask = ~mask;
+  h0 = (h0 & mask) | g0;
+  h1 = (h1 & mask) | g1;
+  h2 = (h2 & mask) | g2;
+  h3 = (h3 & mask) | g3;
+  h4 = (h4 & mask) | g4;
+
+  /* h = h % (2^128) */
+  h0 = ((h0) | (h1 << 26)) & 0xffffffff;
+  h1 = ((h1 >> 6) | (h2 << 20)) & 0xffffffff;
+  h2 = ((h2 >> 12) | (h3 << 14)) & 0xffffffff;
+  h3 = ((h3 >> 18) | (h4 << 8)) & 0xffffffff;
+
+  /* mac = (h + pad) % (2^128) */
+  f = (u64) h0 + st->pad[0];
+  h0 = (u32) f;
+  f = (u64) h1 + st->pad[1] + (f >> 32);
+  h1 = (u32) f;
+  f = (u64) h2 + st->pad[2] + (f >> 32);
+  h2 = (u32) f;
+  f = (u64) h3 + st->pad[3] + (f >> 32);
+  h3 = (u32) f;
+
+  buf_put_le32 (mac + 0, h0);
+  buf_put_le32 (mac + 4, h1);
+  buf_put_le32 (mac + 8, h2);
+  buf_put_le32 (mac + 12, h3);
+
+  /* zero out the state */
+  st->h[0] = 0;
+  st->h[1] = 0;
+  st->h[2] = 0;
+  st->h[3] = 0;
+  st->h[4] = 0;
+  st->r[0] = 0;
+  st->r[1] = 0;
+  st->r[2] = 0;
+  st->r[3] = 0;
+  st->r[4] = 0;
+  st->pad[0] = 0;
+  st->pad[1] = 0;
+  st->pad[2] = 0;
+  st->pad[3] = 0;
+
+  /* burn_stack */
+  return (13 * sizeof (u32) + sizeof (u64) +
+	  POLY1305_REF_BLOCKSIZE + 6 * sizeof (void *)) + burn;
+}
+
+
+static const poly1305_ops_t poly1305_default_ops = {
+  poly1305_block_size_ref32,
+  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 unsigned int
+poly1305_block_size_ref8 (void)
+{
+  return POLY1305_REF_BLOCKSIZE;
+}
+
+
+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_block_size_ref8,
+  poly1305_init_ext_ref8,
+  poly1305_blocks_ref8,
+  poly1305_finish_ext_ref8
+};
+
+#endif /* HAVE_U64_TYPEDEF */
+

+
+
+static inline void *
+poly1305_get_state (poly1305_context_t * ctx)
+{
+  byte *c = ctx->state;
+  c += POLY1305_STATE_ALIGNMENT - 1;
+  c -= (uintptr_t) c & (POLY1305_STATE_ALIGNMENT - 1);
+  return c;
+}
+
+
+static void
+poly1305_init (poly1305_context_t * ctx, const poly1305_key_t * key)
+{
+  void *state = poly1305_get_state (ctx);
+
+  ctx->block_size = ctx->ops->block_size ();
+  ctx->leftover = 0;
+
+  ctx->ops->init_ext (state, key);
+}
+
+
+void
+_gcry_poly1305_update (poly1305_context_t * ctx, const byte * m, size_t bytes)
+{
+  void *state = poly1305_get_state (ctx);
+  unsigned int burn = 0;
+
+  /* handle leftover */
+  if (ctx->leftover)
+    {
+      size_t want = (ctx->block_size - ctx->leftover);
+      if (want > bytes)
+	want = bytes;
+      buf_cpy (ctx->buffer + ctx->leftover, m, want);
+      bytes -= want;
+      m += want;
+      ctx->leftover += want;
+      if (ctx->leftover < ctx->block_size)
+	return;
+      burn = ctx->ops->blocks (state, ctx->buffer, ctx->block_size);
+      ctx->leftover = 0;
+    }
+
+  /* process full blocks */
+  if (bytes >= ctx->block_size)
+    {
+      size_t want = (bytes & ~(ctx->block_size - 1));
+      burn = ctx->ops->blocks (state, m, want);
+      m += want;
+      bytes -= want;
+    }
+
+  /* store leftover */
+  if (bytes)
+    {
+      buf_cpy (ctx->buffer + ctx->leftover, m, bytes);
+      ctx->leftover += bytes;
+    }
+
+  if (burn)
+    _gcry_burn_stack (burn);
+}
+
+
+void
+_gcry_poly1305_finish (poly1305_context_t * ctx, byte mac[POLY1305_TAGLEN])
+{
+  void *state = poly1305_get_state (ctx);
+  unsigned int burn;
+
+  burn = ctx->ops->finish_ext (state, ctx->buffer, ctx->leftover, mac);
+
+  _gcry_burn_stack (burn);
+}
+
+
+gcry_err_code_t
+_gcry_poly1305_init (poly1305_context_t * ctx, const byte * key,
+		     size_t keylen)
+{
+  static int initialized;
+  static const char *selftest_failed;
+  poly1305_key_t keytmp;
+
+  if (!initialized)
+    {
+      initialized = 1;
+      selftest_failed = selftest ();
+      if (selftest_failed)
+	log_error ("Poly1305 selftest failed (%s)\n", selftest_failed);
+    }
+
+  if (keylen != POLY1305_KEYLEN)
+    return GPG_ERR_INV_KEYLEN;
+
+  if (selftest_failed)
+    return GPG_ERR_SELFTEST_FAILED;
+
+  ctx->ops = &poly1305_default_ops;
+
+  buf_cpy (keytmp.b, key, POLY1305_KEYLEN);
+  poly1305_init (ctx, &keytmp);
+
+  wipememory (&keytmp, sizeof (keytmp));
+
+  return 0;
+}
+
+
+static void
+poly1305_auth (byte mac[POLY1305_TAGLEN], const byte * m, size_t bytes,
+	       const byte * key)
+{
+  poly1305_context_t ctx;
+
+  memset (&ctx, 0, sizeof (ctx));
+
+  _gcry_poly1305_init (&ctx, key, POLY1305_KEYLEN);
+  _gcry_poly1305_update (&ctx, m, bytes);
+  _gcry_poly1305_finish (&ctx, mac);
+
+  wipememory (&ctx, sizeof (ctx));
+}
+
+
+static const char *
+selftest (void)
+{
+  /* example from nacl */
+  static const byte nacl_key[POLY1305_KEYLEN] = {
+    0xee, 0xa6, 0xa7, 0x25, 0x1c, 0x1e, 0x72, 0x91,
+    0x6d, 0x11, 0xc2, 0xcb, 0x21, 0x4d, 0x3c, 0x25,
+    0x25, 0x39, 0x12, 0x1d, 0x8e, 0x23, 0x4e, 0x65,
+    0x2d, 0x65, 0x1f, 0xa4, 0xc8, 0xcf, 0xf8, 0x80,
+  };
+
+  static const byte nacl_msg[131] = {
+    0x8e, 0x99, 0x3b, 0x9f, 0x48, 0x68, 0x12, 0x73,
+    0xc2, 0x96, 0x50, 0xba, 0x32, 0xfc, 0x76, 0xce,
+    0x48, 0x33, 0x2e, 0xa7, 0x16, 0x4d, 0x96, 0xa4,
+    0x47, 0x6f, 0xb8, 0xc5, 0x31, 0xa1, 0x18, 0x6a,
+    0xc0, 0xdf, 0xc1, 0x7c, 0x98, 0xdc, 0xe8, 0x7b,
+    0x4d, 0xa7, 0xf0, 0x11, 0xec, 0x48, 0xc9, 0x72,
+    0x71, 0xd2, 0xc2, 0x0f, 0x9b, 0x92, 0x8f, 0xe2,
+    0x27, 0x0d, 0x6f, 0xb8, 0x63, 0xd5, 0x17, 0x38,
+    0xb4, 0x8e, 0xee, 0xe3, 0x14, 0xa7, 0xcc, 0x8a,
+    0xb9, 0x32, 0x16, 0x45, 0x48, 0xe5, 0x26, 0xae,
+    0x90, 0x22, 0x43, 0x68, 0x51, 0x7a, 0xcf, 0xea,
+    0xbd, 0x6b, 0xb3, 0x73, 0x2b, 0xc0, 0xe9, 0xda,
+    0x99, 0x83, 0x2b, 0x61, 0xca, 0x01, 0xb6, 0xde,
+    0x56, 0x24, 0x4a, 0x9e, 0x88, 0xd5, 0xf9, 0xb3,
+    0x79, 0x73, 0xf6, 0x22, 0xa4, 0x3d, 0x14, 0xa6,
+    0x59, 0x9b, 0x1f, 0x65, 0x4c, 0xb4, 0x5a, 0x74,
+    0xe3, 0x55, 0xa5
+  };
+
+  static const byte nacl_mac[16] = {
+    0xf3, 0xff, 0xc7, 0x70, 0x3f, 0x94, 0x00, 0xe5,
+    0x2a, 0x7d, 0xfb, 0x4b, 0x3d, 0x33, 0x05, 0xd9
+  };
+
+  /* generates a final value of (2^130 - 2) == 3 */
+  static const byte wrap_key[POLY1305_KEYLEN] = {
+    0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+
+  static const byte wrap_msg[16] = {
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+  };
+
+  static const byte wrap_mac[16] = {
+    0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+  };
+
+  /* mac of the macs of messages of length 0 to 256, where the key and messages
+   * have all their values set to the length
+   */
+  static const byte total_key[POLY1305_KEYLEN] = {
+    0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+    0xff, 0xfe, 0xfd, 0xfc, 0xfb, 0xfa, 0xf9,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+  };
+
+  static const byte total_mac[16] = {
+    0x64, 0xaf, 0xe2, 0xe8, 0xd6, 0xad, 0x7b, 0xbd,
+    0xd2, 0x87, 0xf9, 0x7c, 0x44, 0x62, 0x3d, 0x39
+  };
+
+  poly1305_context_t ctx;
+  poly1305_context_t total_ctx;
+  byte all_key[POLY1305_KEYLEN];
+  byte all_msg[256];
+  byte mac[16];
+  size_t i, j;
+
+  memset (&ctx, 0, sizeof (ctx));
+  memset (&total_ctx, 0, sizeof (total_ctx));
+
+  memset (mac, 0, sizeof (mac));
+  poly1305_auth (mac, nacl_msg, sizeof (nacl_msg), nacl_key);
+  if (memcmp (nacl_mac, mac, sizeof (nacl_mac)) != 0)
+    return "Poly1305 test 1 failed.";
+
+  /* SSE2/AVX have a 32 byte block size, but also support 64 byte blocks, so
+   * make sure everything still works varying between them */
+  memset (mac, 0, sizeof (mac));
+  _gcry_poly1305_init (&ctx, nacl_key, POLY1305_KEYLEN);
+  _gcry_poly1305_update (&ctx, nacl_msg + 0, 32);
+  _gcry_poly1305_update (&ctx, nacl_msg + 32, 64);
+  _gcry_poly1305_update (&ctx, nacl_msg + 96, 16);
+  _gcry_poly1305_update (&ctx, nacl_msg + 112, 8);
+  _gcry_poly1305_update (&ctx, nacl_msg + 120, 4);
+  _gcry_poly1305_update (&ctx, nacl_msg + 124, 2);
+  _gcry_poly1305_update (&ctx, nacl_msg + 126, 1);
+  _gcry_poly1305_update (&ctx, nacl_msg + 127, 1);
+  _gcry_poly1305_update (&ctx, nacl_msg + 128, 1);
+  _gcry_poly1305_update (&ctx, nacl_msg + 129, 1);
+  _gcry_poly1305_update (&ctx, nacl_msg + 130, 1);
+  _gcry_poly1305_finish (&ctx, mac);
+  if (memcmp (nacl_mac, mac, sizeof (nacl_mac)) != 0)
+    return "Poly1305 test 2 failed.";
+
+  memset (mac, 0, sizeof (mac));
+  poly1305_auth (mac, wrap_msg, sizeof (wrap_msg), wrap_key);
+  if (memcmp (wrap_mac, mac, sizeof (nacl_mac)) != 0)
+    return "Poly1305 test 3 failed.";
+
+  _gcry_poly1305_init (&total_ctx, total_key, POLY1305_KEYLEN);
+  for (i = 0; i < 256; i++)
+    {
+      /* set key and message to 'i,i,i..' */
+      for (j = 0; j < sizeof (all_key); j++)
+	all_key[j] = i;
+      for (j = 0; j < i; j++)
+	all_msg[j] = i;
+      poly1305_auth (mac, all_msg, i, all_key);
+      _gcry_poly1305_update (&total_ctx, mac, 16);
+    }
+  _gcry_poly1305_finish (&total_ctx, mac);
+  if (memcmp (total_mac, mac, sizeof (total_mac)) != 0)
+    return "Poly1305 test 4 failed.";
+
+  return NULL;
+}
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index d4e9bb2..8648e96 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1341,7 +1341,9 @@ enum gcry_mac_algos
     GCRY_MAC_GMAC_CAMELLIA      = 402,
     GCRY_MAC_GMAC_TWOFISH       = 403,
     GCRY_MAC_GMAC_SERPENT       = 404,
-    GCRY_MAC_GMAC_SEED          = 405
+    GCRY_MAC_GMAC_SEED          = 405,
+
+    GCRY_MAC_POLY1305           = 501
   };
 
 /* Flags used with the open function.  */
diff --git a/tests/basic.c b/tests/basic.c
index 406d82d..cc0f4c1 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -5041,6 +5041,8 @@ check_mac (void)
     const char *key;
     const char *expect;
     const char *iv;
+    unsigned int dlen;
+    unsigned int klen;
   } algos[] =
     {
       { GCRY_MAC_HMAC_MD5, "what do ya want for nothing?", "Jefe",
@@ -5491,6 +5493,109 @@ check_mac (void)
         "\xc9\xfc\xa7\x29\xab\x60\xad\xa0",
         "\x20\x4b\xdb\x1b\xd6\x21\x54\xbf\x08\x92\x2a\xaa\x54\xee\xd7\x05",
         "\x05\xad\x13\xa5\xe2\xc2\xab\x66\x7e\x1a\x6f\xbc" },
+      /* from NaCl */
+      { GCRY_MAC_POLY1305,
+        "\x8e\x99\x3b\x9f\x48\x68\x12\x73\xc2\x96\x50\xba\x32\xfc\x76\xce"
+        "\x48\x33\x2e\xa7\x16\x4d\x96\xa4\x47\x6f\xb8\xc5\x31\xa1\x18\x6a"
+        "\xc0\xdf\xc1\x7c\x98\xdc\xe8\x7b\x4d\xa7\xf0\x11\xec\x48\xc9\x72"
+        "\x71\xd2\xc2\x0f\x9b\x92\x8f\xe2\x27\x0d\x6f\xb8\x63\xd5\x17\x38"
+        "\xb4\x8e\xee\xe3\x14\xa7\xcc\x8a\xb9\x32\x16\x45\x48\xe5\x26\xae"
+        "\x90\x22\x43\x68\x51\x7a\xcf\xea\xbd\x6b\xb3\x73\x2b\xc0\xe9\xda"
+        "\x99\x83\x2b\x61\xca\x01\xb6\xde\x56\x24\x4a\x9e\x88\xd5\xf9\xb3"
+        "\x79\x73\xf6\x22\xa4\x3d\x14\xa6\x59\x9b\x1f\x65\x4c\xb4\x5a\x74"
+        "\xe3\x55\xa5",
+        "\xee\xa6\xa7\x25\x1c\x1e\x72\x91\x6d\x11\xc2\xcb\x21\x4d\x3c\x25"
+        "\x25\x39\x12\x1d\x8e\x23\x4e\x65\x2d\x65\x1f\xa4\xc8\xcf\xf8\x80",
+        "\xf3\xff\xc7\x70\x3f\x94\x00\xe5\x2a\x7d\xfb\x4b\x3d\x33\x05\xd9" },
+      /* from draft-nir-cfrg-chacha20-poly1305-03 */
+      { GCRY_MAC_POLY1305,
+        "Cryptographic Forum Research Group",
+        "\x85\xd6\xbe\x78\x57\x55\x6d\x33\x7f\x44\x52\xfe\x42\xd5\x06\xa8"
+        "\x01\x03\x80\x8a\xfb\x0d\xb2\xfd\x4a\xbf\xf6\xaf\x41\x49\xf5\x1b",
+        "\xa8\x06\x1d\xc1\x30\x51\x36\xc6\xc2\x2b\x8b\xaf\x0c\x01\x27\xa9" },
+      { GCRY_MAC_POLY1305,
+        "'Twas brillig, and the slithy toves\n"
+        "Did gyre and gimble in the wabe:\n"
+        "All mimsy were the borogoves,\n"
+        "And the mome raths outgrabe.",
+        "\x1c\x92\x40\xa5\xeb\x55\xd3\x8a\xf3\x33\x88\x86\x04\xf6\xb5\xf0"
+        "\x47\x39\x17\xc1\x40\x2b\x80\x09\x9d\xca\x5c\xbc\x20\x70\x75\xc0",
+        "\x45\x41\x66\x9a\x7e\xaa\xee\x61\xe7\x08\xdc\x7c\xbc\xc5\xeb\x62" },
+      { GCRY_MAC_POLY1305,
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+        NULL,
+        191, 32 },
+      { GCRY_MAC_POLY1305,
+        "Any submission to the IETF intended by the Contributor for "
+        "publication as all or part of an IETF Internet-Draft or RFC and "
+        "any statement made within the context of an IETF activity is "
+        "considered an \"IETF Contribution\". Such statements include "
+        "oral statements in IETF sessions, as well as written and "
+        "electronic communications made at any time or place, which are "
+        "addressed to",
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+        "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70\xf0\xef\xca\x96\x22\x7a\x86\x3e",
+        "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70\xf0\xef\xca\x96\x22\x7a\x86\x3e",
+        NULL,
+        0, 32 },
+      { GCRY_MAC_POLY1305,
+        "Any submission to the IETF intended by the Contributor for "
+        "publication as all or part of an IETF Internet-Draft or RFC and "
+        "any statement made within the context of an IETF activity is "
+        "considered an \"IETF Contribution\". Such statements include "
+        "oral statements in IETF sessions, as well as written and "
+        "electronic communications made at any time or place, which are "
+        "addressed to",
+        "\x36\xe5\xf6\xb5\xc5\xe0\x60\x70\xf0\xef\xca\x96\x22\x7a\x86\x3e"
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
+        "\xf3\x47\x7e\x7c\xd9\x54\x17\xaf\x89\xa6\xb8\x79\x4c\x31\x0c\xf0",
+        NULL,
+        0, 32 },
+      /* from http://cr.yp.to/mac/poly1305-20050329.pdf */
+      { GCRY_MAC_POLY1305,
+        "\xf3\xf6",
+        "\x85\x1f\xc4\x0c\x34\x67\xac\x0b\xe0\x5c\xc2\x04\x04\xf3\xf7\x00"
+        "\x58\x0b\x3b\x0f\x94\x47\xbb\x1e\x69\xd0\x95\xb5\x92\x8b\x6d\xbc",
+        "\xf4\xc6\x33\xc3\x04\x4f\xc1\x45\xf8\x4f\x33\x5c\xb8\x19\x53\xde",
+        NULL,
+        0, 32 },
+      { GCRY_MAC_POLY1305,
+        "",
+        "\xa0\xf3\x08\x00\x00\xf4\x64\x00\xd0\xc7\xe9\x07\x6c\x83\x44\x03"
+        "\xdd\x3f\xab\x22\x51\xf1\x1a\xc7\x59\xf0\x88\x71\x29\xcc\x2e\xe7",
+        "\xdd\x3f\xab\x22\x51\xf1\x1a\xc7\x59\xf0\x88\x71\x29\xcc\x2e\xe7",
+        NULL,
+        0, 32 },
+      { GCRY_MAC_POLY1305,
+        "\x66\x3c\xea\x19\x0f\xfb\x83\xd8\x95\x93\xf3\xf4\x76\xb6\xbc\x24"
+        "\xd7\xe6\x79\x10\x7e\xa2\x6a\xdb\x8c\xaf\x66\x52\xd0\x65\x61\x36",
+        "\x48\x44\x3d\x0b\xb0\xd2\x11\x09\xc8\x9a\x10\x0b\x5c\xe2\xc2\x08"
+        "\x83\x14\x9c\x69\xb5\x61\xdd\x88\x29\x8a\x17\x98\xb1\x07\x16\xef",
+        "\x0e\xe1\xc1\x6b\xb7\x3f\x0f\x4f\xd1\x98\x81\x75\x3c\x01\xcd\xbe",
+        NULL,
+        0, 32 },
+      { GCRY_MAC_POLY1305,
+        "\xab\x08\x12\x72\x4a\x7f\x1e\x34\x27\x42\xcb\xed\x37\x4d\x94\xd1"
+        "\x36\xc6\xb8\x79\x5d\x45\xb3\x81\x98\x30\xf2\xc0\x44\x91\xfa\xf0"
+        "\x99\x0c\x62\xe4\x8b\x80\x18\xb2\xc3\xe4\xa0\xfa\x31\x34\xcb\x67"
+        "\xfa\x83\xe1\x58\xc9\x94\xd9\x61\xc4\xcb\x21\x09\x5c\x1b\xf9",
+        "\x12\x97\x6a\x08\xc4\x42\x6d\x0c\xe8\xa8\x24\x07\xc4\xf4\x82\x07"
+        "\x80\xf8\xc2\x0a\xa7\x12\x02\xd1\xe2\x91\x79\xcb\xcb\x55\x5a\x57",
+        "\x51\x54\xad\x0d\x2c\xb2\x6e\x01\x27\x4f\xc5\x11\x48\x49\x1f\x1b" },
       { 0 },
     };
   int i;
@@ -5500,6 +5605,8 @@ check_mac (void)
 
   for (i = 0; algos[i].algo; i++)
     {
+      size_t klen, dlen;
+
       if (gcry_mac_test_algo (algos[i].algo))
         {
           show_mac_not_available (algos[i].algo);
@@ -5520,13 +5627,14 @@ check_mac (void)
 		 algos[i].algo,
 		 (int)strlen(algos[i].key), (int)strlen(algos[i].data));
 
-      check_one_mac (algos[i].algo, algos[i].data, strlen (algos[i].data),
-		     algos[i].key, strlen(algos[i].key), algos[i].iv,
-		     algos[i].iv ? strlen(algos[i].iv) : 0,
+      klen = algos[i].klen ? algos[i].klen : strlen(algos[i].key);
+      dlen = algos[i].dlen ? algos[i].dlen : strlen (algos[i].data);
+
+      check_one_mac (algos[i].algo, algos[i].data, dlen, algos[i].key, klen,
+		     algos[i].iv, algos[i].iv ? strlen(algos[i].iv) : 0,
 		     algos[i].expect, 0);
-      check_one_mac (algos[i].algo, algos[i].data, strlen (algos[i].data),
-		     algos[i].key, strlen(algos[i].key), algos[i].iv,
-		     algos[i].iv ? strlen(algos[i].iv) : 0,
+      check_one_mac (algos[i].algo, algos[i].data, dlen, algos[i].key, klen,
+		     algos[i].iv, algos[i].iv ? strlen(algos[i].iv) : 0,
 		     algos[i].expect, 1);
     }
 
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index bd05064..3d8ae37 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -1405,7 +1405,7 @@ mac_bench (char **argv, int argc)
     }
   else
     {
-      for (i = 1; i < 500; i++)
+      for (i = 1; i < 600; i++)
 	if (!gcry_mac_test_algo (i))
 	  _mac_bench (i);
     }
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 5efc083..9fd716d 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -461,7 +461,7 @@ mac_bench ( const char *algoname )
 
   if (!algoname)
     {
-      for (i=1; i < 500; i++)
+      for (i=1; i < 600; i++)
         if (in_fips_mode && i == GCRY_MAC_HMAC_MD5)
           ; /* Don't use MD5 in fips mode.  */
         else if ( !gcry_mac_test_algo (i) )




More information about the Gcrypt-devel mailing list