[git] GCRYPT - branch, master, updated. libgcrypt-1.6.0-76-ge813958

by Jussi Kivilinna cvs at cvs.gnupg.org
Fri May 16 20:22:56 CEST 2014


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "The GNU crypto library".

The branch, master has been updated
       via  e813958419b0ec4439e6caf07d3b2234cffa2bfa (commit)
       via  73b3b75c2221a6e3bed4117e0a206a1193acd2ed (commit)
       via  b8794fed68ebe7567f4617141f0996ad290d9120 (commit)
      from  c20daeeb05329bfc6cc2c562cbd4b965291fe0e1 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit e813958419b0ec4439e6caf07d3b2234cffa2bfa
Author: Jussi Kivilinna <jussi.kivilinna at iki.fi>
Date:   Sun May 11 12:00:19 2014 +0300

    Add Poly1305 based cipher AEAD mode
    
    * cipher/Makefile.am: Add 'cipher-poly1305.c'.
    * cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_mode.poly1305'.
    (_gcry_cipher_poly1305_encrypt, _gcry_cipher_poly1305_decrypt)
    (_gcry_cipher_poly1305_setiv, _gcry_cipher_poly1305_authenticate)
    (_gcry_cipher_poly1305_get_tag, _gcry_cipher_poly1305_check_tag): New.
    * cipher/cipher-poly1305.c: New.
    * cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
    (cipher_reset, cipher_encrypt, cipher_decrypt, _gcry_cipher_setiv)
    (_gcry_cipher_authenticate, _gcry_cipher_gettag)
    (_gcry_cipher_checktag): Handle 'GCRY_CIPHER_MODE_POLY1305'.
    (cipher_setiv): Move handling of 'GCRY_CIPHER_MODE_GCM' to ...
    (_gcry_cipher_setiv): ... here, as with other modes.
    * src/gcrypt.h.in: Add 'GCRY_CIPHER_MODE_POLY1305'.
    * tests/basic.c (_check_poly1305_cipher, check_poly1305_cipher): New.
    (check_ciphers): Add Poly1305 check.
    (check_cipher_modes): Call 'check_poly1305_cipher'.
    * tests/bench-slope.c (bench_gcm_encrypt_do_bench): Rename to
    bench_aead_... and take nonce as argument.
    (bench_gcm_decrypt_do_bench, bench_gcm_authenticate_do_bench): Ditto.
    (bench_gcm_encrypt_do_bench, bench_gcm_decrypt_do_bench)
    (bench_gcm_authenticate_do_bench, bench_poly1305_encrypt_do_bench)
    (bench_poly1305_decrypt_do_bench)
    (bench_poly1305_authenticate_do_bench, poly1305_encrypt_ops)
    (poly1305_decrypt_ops, poly1305_authenticate_ops): New.
    (cipher_modes): Add Poly1305.
    (cipher_bench_one): Add special handling for Poly1305.
    --
    
    Patch adds Poly1305 based AEAD cipher mode to libgcrypt. ChaCha20 variant
    of this mode is proposed for use in TLS and ipsec:
     https://tools.ietf.org/html/draft-agl-tls-chacha20poly1305-04
     http://tools.ietf.org/html/draft-nir-ipsecme-chacha20-poly1305-02
    
    Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>

diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index a8b86e6..4468647 100644
--- a/cipher/Makefile.am
+++ b/cipher/Makefile.am
@@ -40,7 +40,7 @@ libcipher_la_LIBADD = $(GCRYPT_MODULES)
 libcipher_la_SOURCES = \
 cipher.c cipher-internal.h \
 cipher-cbc.c cipher-cfb.c cipher-ofb.c cipher-ctr.c cipher-aeswrap.c \
-cipher-ccm.c cipher-cmac.c cipher-gcm.c \
+cipher-ccm.c cipher-cmac.c cipher-gcm.c cipher-poly1305.c \
 cipher-selftest.c cipher-selftest.h \
 pubkey.c pubkey-internal.h pubkey-util.c \
 md.c \
diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h
index cdac445..f6bda66 100644
--- a/cipher/cipher-internal.h
+++ b/cipher/cipher-internal.h
@@ -20,6 +20,9 @@
 #ifndef G10_CIPHER_INTERNAL_H
 #define G10_CIPHER_INTERNAL_H
 
+#include "./poly1305-internal.h"
+
+
 /* The maximum supported size of a block in bytes.  */
 #define MAX_BLOCKSIZE 16
 
@@ -154,6 +157,17 @@ struct gcry_cipher_handle
     } ccm;
 #endif
 
+    /* Mode specific storage for Poly1305 mode. */
+    struct {
+      /* byte counter for AAD and data. */
+      u32 bytecount[2];
+
+      unsigned int aad_finalized:1;
+      unsigned int bytecount_over_limits:1;
+
+      poly1305_context_t ctx;
+    } poly1305;
+
     /* Mode specific storage for CMAC mode. */
     struct {
       unsigned int tag:1; /* Set to 1 if tag has been finalized.  */
@@ -319,4 +333,28 @@ void _gcry_cipher_gcm_setkey
 /*           */   (gcry_cipher_hd_t c);
 
 
+/*-- cipher-poly1305.c --*/
+gcry_err_code_t _gcry_cipher_poly1305_encrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, size_t outbuflen,
+                   const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_poly1305_decrypt
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outbuf, size_t outbuflen,
+                   const unsigned char *inbuf, size_t inbuflen);
+gcry_err_code_t _gcry_cipher_poly1305_setiv
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *iv, size_t ivlen);
+gcry_err_code_t _gcry_cipher_poly1305_authenticate
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *aadbuf, size_t aadbuflen);
+gcry_err_code_t _gcry_cipher_poly1305_get_tag
+/*           */   (gcry_cipher_hd_t c,
+                   unsigned char *outtag, size_t taglen);
+gcry_err_code_t _gcry_cipher_poly1305_check_tag
+/*           */   (gcry_cipher_hd_t c,
+                   const unsigned char *intag, size_t taglen);
+void _gcry_cipher_poly1305_setkey
+/*           */   (gcry_cipher_hd_t c);
+
 #endif /*G10_CIPHER_INTERNAL_H*/
diff --git a/cipher/cipher-poly1305.c b/cipher/cipher-poly1305.c
new file mode 100644
index 0000000..a22ffa3
--- /dev/null
+++ b/cipher/cipher-poly1305.c
@@ -0,0 +1,296 @@
+/* cipher-pol1305.c  -  Poly1305 based AEAD cipher mode
+ * 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 "cipher.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+#include "./poly1305-internal.h"
+
+
+static inline int
+poly1305_bytecounter_add (u32 ctr[2], size_t add)
+{
+  int overflow = 0;
+
+  if (sizeof(add) > sizeof(u32))
+    {
+      u32 high_add = ((add >> 31) >> 1) & 0xffffffff;
+      ctr[1] += high_add;
+      if (ctr[1] < high_add)
+        overflow = 1;
+    }
+
+  ctr[0] += add;
+  if (ctr[0] >= add)
+    return overflow;
+
+  ctr[1] += 1;
+  return (ctr[1] < 1) || overflow;
+}
+
+
+static void
+poly1305_fill_bytecount (gcry_cipher_hd_t c)
+{
+  u32 lenbuf[2];
+
+  lenbuf[0] = le_bswap32(c->u_mode.poly1305.bytecount[0]);
+  lenbuf[1] = le_bswap32(c->u_mode.poly1305.bytecount[1]);
+  _gcry_poly1305_update (&c->u_mode.poly1305.ctx, (byte*)lenbuf,
+			 sizeof(lenbuf));
+
+  wipememory(lenbuf, sizeof(lenbuf));
+}
+
+
+static void
+poly1305_aad_finish (gcry_cipher_hd_t c)
+{
+  /* Start of encryption marks end of AAD stream. */
+  poly1305_fill_bytecount(c);
+
+  c->u_mode.poly1305.aad_finalized = 1;
+
+  c->u_mode.poly1305.bytecount[0] = 0;
+  c->u_mode.poly1305.bytecount[1] = 0;
+}
+
+
+static gcry_err_code_t
+poly1305_set_zeroiv (gcry_cipher_hd_t c)
+{
+  byte zero[8] = { 0, };
+
+  return _gcry_cipher_poly1305_setiv (c, zero, sizeof(zero));
+}
+
+
+gcry_err_code_t
+_gcry_cipher_poly1305_authenticate (gcry_cipher_hd_t c,
+				    const byte * aadbuf, size_t aadbuflen)
+{
+  if (c->u_mode.poly1305.bytecount_over_limits)
+    return GPG_ERR_INV_LENGTH;
+  if (c->u_mode.poly1305.aad_finalized)
+    return GPG_ERR_INV_STATE;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+
+  if (!c->marks.iv)
+    poly1305_set_zeroiv(c);
+
+  if (poly1305_bytecounter_add(c->u_mode.poly1305.bytecount, aadbuflen))
+    {
+      c->u_mode.poly1305.bytecount_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  _gcry_poly1305_update (&c->u_mode.poly1305.ctx, aadbuf, aadbuflen);
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_poly1305_encrypt (gcry_cipher_hd_t c,
+			       byte *outbuf, size_t outbuflen,
+			       const byte *inbuf, size_t inbuflen)
+{
+  gcry_err_code_t err;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+  if (c->u_mode.poly1305.bytecount_over_limits)
+    return GPG_ERR_INV_LENGTH;
+
+  if (!c->marks.iv)
+    {
+      err = poly1305_set_zeroiv(c);
+      if (err)
+        return err;
+    }
+
+  if (!c->u_mode.poly1305.aad_finalized)
+    poly1305_aad_finish(c);
+
+  if (poly1305_bytecounter_add(c->u_mode.poly1305.bytecount, inbuflen))
+    {
+      c->u_mode.poly1305.bytecount_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  c->spec->stencrypt(&c->context.c, outbuf, (byte*)inbuf, inbuflen);
+
+  _gcry_poly1305_update (&c->u_mode.poly1305.ctx, outbuf, inbuflen);
+
+  return 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_poly1305_decrypt (gcry_cipher_hd_t c,
+			       byte *outbuf, size_t outbuflen,
+			       const byte *inbuf, size_t inbuflen)
+{
+  gcry_err_code_t err;
+
+  if (outbuflen < inbuflen)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->marks.tag)
+    return GPG_ERR_INV_STATE;
+  if (c->u_mode.poly1305.bytecount_over_limits)
+    return GPG_ERR_INV_LENGTH;
+
+  if (!c->marks.iv)
+    {
+      err = poly1305_set_zeroiv(c);
+      if (err)
+        return err;
+    }
+
+  if (!c->u_mode.poly1305.aad_finalized)
+    poly1305_aad_finish(c);
+
+  if (poly1305_bytecounter_add(c->u_mode.poly1305.bytecount, inbuflen))
+    {
+      c->u_mode.poly1305.bytecount_over_limits = 1;
+      return GPG_ERR_INV_LENGTH;
+    }
+
+  _gcry_poly1305_update (&c->u_mode.poly1305.ctx, inbuf, inbuflen);
+
+  c->spec->stdecrypt(&c->context.c, outbuf, (byte*)inbuf, inbuflen);
+  return 0;
+}
+
+
+static gcry_err_code_t
+_gcry_cipher_poly1305_tag (gcry_cipher_hd_t c,
+			   byte * outbuf, size_t outbuflen, int check)
+{
+  gcry_err_code_t err;
+
+  if (outbuflen < GCRY_GCM_BLOCK_LEN)
+    return GPG_ERR_BUFFER_TOO_SHORT;
+  if (c->u_mode.poly1305.bytecount_over_limits)
+    return GPG_ERR_INV_LENGTH;
+
+  if (!c->marks.iv)
+    {
+      err = poly1305_set_zeroiv(c);
+      if (err)
+        return err;
+    }
+
+  if (!c->u_mode.poly1305.aad_finalized)
+    poly1305_aad_finish(c);
+
+  if (!c->marks.tag)
+    {
+      /* Write data-length to poly1305. */
+      poly1305_fill_bytecount(c);
+
+      _gcry_poly1305_finish(&c->u_mode.poly1305.ctx, c->u_iv.iv);
+
+      c->marks.tag = 1;
+    }
+
+  if (check)
+    return buf_eq_const(outbuf, c->u_iv.iv, outbuflen) ?
+           GPG_ERR_NO_ERROR : GPG_ERR_CHECKSUM;
+
+  memcpy (outbuf, c->u_iv.iv, outbuflen);
+  return GPG_ERR_NO_ERROR;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_poly1305_get_tag (gcry_cipher_hd_t c, unsigned char *outtag,
+                          size_t taglen)
+{
+  return _gcry_cipher_poly1305_tag (c, outtag, taglen, 0);
+}
+
+gcry_err_code_t
+_gcry_cipher_poly1305_check_tag (gcry_cipher_hd_t c, const unsigned char *intag,
+                            size_t taglen)
+{
+  return _gcry_cipher_poly1305_tag (c, (unsigned char *) intag, taglen, 1);
+}
+
+
+void
+_gcry_cipher_poly1305_setkey (gcry_cipher_hd_t c)
+{
+  c->u_mode.poly1305.bytecount[0] = 0;
+  c->u_mode.poly1305.bytecount[1] = 0;
+
+  c->u_mode.poly1305.bytecount_over_limits = 0;
+  c->u_mode.poly1305.aad_finalized = 0;
+  c->marks.tag = 0;
+  c->marks.iv = 0;
+}
+
+
+gcry_err_code_t
+_gcry_cipher_poly1305_setiv (gcry_cipher_hd_t c, const byte *iv, size_t ivlen)
+{
+  byte tmpbuf[64]; /* size of ChaCha20/Salsa20 block */
+  gcry_err_code_t err;
+
+  if (!iv && ivlen > 0)
+    return GPG_ERR_INV_ARG;
+
+  memset(&c->u_mode.poly1305.ctx, 0, sizeof(c->u_mode.poly1305.ctx));
+
+  c->u_mode.poly1305.bytecount[0] = 0;
+  c->u_mode.poly1305.bytecount[1] = 0;
+
+  c->u_mode.poly1305.bytecount_over_limits = 0;
+  c->u_mode.poly1305.aad_finalized = 0;
+  c->marks.tag = 0;
+  c->marks.iv = 0;
+
+  /* Set up IV for stream cipher. */
+  c->spec->setiv (&c->context.c, iv, ivlen);
+
+  /* Get the first block from ChaCha20/Salsa20. */
+  memset(tmpbuf, 0, sizeof(tmpbuf));
+  c->spec->stencrypt(&c->context.c, tmpbuf, tmpbuf, sizeof(tmpbuf));
+
+  /* Use the first 32-bytes as Poly1305 key. */
+  err = _gcry_poly1305_init (&c->u_mode.poly1305.ctx, tmpbuf, POLY1305_KEYLEN);
+
+  wipememory(tmpbuf, sizeof(tmpbuf));
+
+  if (err)
+    return err;
+
+  c->marks.iv = 1;
+  return 0;
+}
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 4751302..da59061 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -418,6 +418,15 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
 	  err = GPG_ERR_INV_CIPHER_MODE;
 	break;
 
+      case GCRY_CIPHER_MODE_POLY1305:
+	if (!spec->stencrypt || !spec->stdecrypt || !spec->setiv)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	else if (spec->algo != GCRY_CIPHER_SALSA20 &&
+		 spec->algo != GCRY_CIPHER_SALSA20R12 &&
+		 spec->algo != GCRY_CIPHER_CHACHA20)
+	  err = GPG_ERR_INV_CIPHER_MODE;
+	break;
+
       case GCRY_CIPHER_MODE_STREAM:
 	if (!spec->stencrypt || !spec->stdecrypt)
 	  err = GPG_ERR_INV_CIPHER_MODE;
@@ -611,6 +620,10 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
           _gcry_cipher_gcm_setkey (c);
           break;
 
+        case GCRY_CIPHER_MODE_POLY1305:
+          _gcry_cipher_poly1305_setkey (c);
+          break;
+
         default:
           break;
         };
@@ -627,10 +640,6 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
 static gcry_err_code_t
 cipher_setiv (gcry_cipher_hd_t c, const byte *iv, size_t ivlen)
 {
-  /* GCM has its own IV handler */
-  if (c->mode == GCRY_CIPHER_MODE_GCM)
-    return _gcry_cipher_gcm_setiv (c, iv, ivlen);
-
   /* If the cipher has its own IV handler, we use only this one.  This
      is currently used for stream ciphers requiring a nonce.  */
   if (c->spec->setiv)
@@ -699,6 +708,10 @@ cipher_reset (gcry_cipher_hd_t c)
       }
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      memset (&c->u_mode.poly1305, 0, sizeof c->u_mode.poly1305);
+      break;
+
 #ifdef HAVE_U64_TYPEDEF
     case GCRY_CIPHER_MODE_CCM:
       memset (&c->u_mode.ccm, 0, sizeof c->u_mode.ccm);
@@ -811,6 +824,11 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_gcm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      rc = _gcry_cipher_poly1305_encrypt (c, outbuf, outbuflen,
+					  inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stencrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -919,6 +937,11 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, size_t outbuflen,
       rc = _gcry_cipher_gcm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      rc = _gcry_cipher_poly1305_decrypt (c, outbuf, outbuflen,
+					  inbuf, inbuflen);
+      break;
+
     case GCRY_CIPHER_MODE_STREAM:
       c->spec->stdecrypt (&c->context.c,
                           outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -1000,6 +1023,14 @@ _gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
         rc = _gcry_cipher_ccm_set_nonce (hd, iv, ivlen);
         break;
 
+      case GCRY_CIPHER_MODE_GCM:
+        rc =  _gcry_cipher_gcm_setiv (hd, iv, ivlen);
+        break;
+
+      case GCRY_CIPHER_MODE_POLY1305:
+        rc =  _gcry_cipher_poly1305_setiv (hd, iv, ivlen);
+        break;
+
       default:
         rc = cipher_setiv (hd, iv, ivlen);
         break;
@@ -1050,6 +1081,10 @@ _gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
       rc = _gcry_cipher_gcm_authenticate (hd, abuf, abuflen);
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      rc = _gcry_cipher_poly1305_authenticate (hd, abuf, abuflen);
+      break;
+
     default:
       log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -1079,6 +1114,10 @@ _gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
       rc = _gcry_cipher_gcm_get_tag (hd, outtag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      rc = _gcry_cipher_poly1305_get_tag (hd, outtag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
@@ -1108,6 +1147,10 @@ _gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
       rc = _gcry_cipher_gcm_check_tag (hd, intag, taglen);
       break;
 
+    case GCRY_CIPHER_MODE_POLY1305:
+      rc = _gcry_cipher_poly1305_check_tag (hd, intag, taglen);
+      break;
+
     default:
       log_error ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
       rc = GPG_ERR_INV_CIPHER_MODE;
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 3145020..bd38a24 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -895,16 +895,17 @@ enum gcry_cipher_algos
    supported for each algorithm. */
 enum gcry_cipher_modes
   {
-    GCRY_CIPHER_MODE_NONE   = 0,  /* Not yet specified. */
-    GCRY_CIPHER_MODE_ECB    = 1,  /* Electronic codebook. */
-    GCRY_CIPHER_MODE_CFB    = 2,  /* Cipher feedback. */
-    GCRY_CIPHER_MODE_CBC    = 3,  /* Cipher block chaining. */
-    GCRY_CIPHER_MODE_STREAM = 4,  /* Used with stream ciphers. */
-    GCRY_CIPHER_MODE_OFB    = 5,  /* Outer feedback. */
-    GCRY_CIPHER_MODE_CTR    = 6,  /* Counter. */
-    GCRY_CIPHER_MODE_AESWRAP= 7,  /* AES-WRAP algorithm.  */
-    GCRY_CIPHER_MODE_CCM    = 8,  /* Counter with CBC-MAC.  */
-    GCRY_CIPHER_MODE_GCM    = 9   /* Galois Counter Mode. */
+    GCRY_CIPHER_MODE_NONE     = 0,   /* Not yet specified. */
+    GCRY_CIPHER_MODE_ECB      = 1,   /* Electronic codebook. */
+    GCRY_CIPHER_MODE_CFB      = 2,   /* Cipher feedback. */
+    GCRY_CIPHER_MODE_CBC      = 3,   /* Cipher block chaining. */
+    GCRY_CIPHER_MODE_STREAM   = 4,   /* Used with stream ciphers. */
+    GCRY_CIPHER_MODE_OFB      = 5,   /* Outer feedback. */
+    GCRY_CIPHER_MODE_CTR      = 6,   /* Counter. */
+    GCRY_CIPHER_MODE_AESWRAP  = 7,   /* AES-WRAP algorithm.  */
+    GCRY_CIPHER_MODE_CCM      = 8,   /* Counter with CBC-MAC.  */
+    GCRY_CIPHER_MODE_GCM      = 9,   /* Galois Counter Mode. */
+    GCRY_CIPHER_MODE_POLY1305 = 10,  /* Poly1305 based AEAD mode. */
   };
 
 /* Flags used with the open function. */
diff --git a/tests/basic.c b/tests/basic.c
index de10617..9740919 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -1620,6 +1620,320 @@ check_gcm_cipher (void)
 
 
 static void
+_check_poly1305_cipher (unsigned int step)
+{
+  struct tv
+  {
+    int algo;
+    char key[MAX_DATA_LEN];
+    char iv[MAX_DATA_LEN];
+    int ivlen;
+    unsigned char aad[MAX_DATA_LEN];
+    int aadlen;
+    unsigned char plaintext[MAX_DATA_LEN];
+    int inlen;
+    char out[MAX_DATA_LEN];
+    char tag[MAX_DATA_LEN];
+  } tv[] =
+    {
+      /* draft-agl-tls-chacha20poly1305-04 */
+      { GCRY_CIPHER_CHACHA20,
+        "\x42\x90\xbc\xb1\x54\x17\x35\x31\xf3\x14\xaf\x57\xf3\xbe\x3b\x50"
+	"\x06\xda\x37\x1e\xce\x27\x2a\xfa\x1b\x5d\xbd\xd1\x10\x0a\x10\x07",
+        "\xcd\x7c\xf6\x7b\xe3\x9c\x79\x4a", 8,
+        "\x87\xe2\x29\xd4\x50\x08\x45\xa0\x79\xc0", 10,
+        "\x86\xd0\x99\x74\x84\x0b\xde\xd2\xa5\xca", 10,
+        "\xe3\xe4\x46\xf7\xed\xe9\xa1\x9b\x62\xa4",
+        "\x67\x7d\xab\xf4\xe3\xd2\x4b\x87\x6b\xb2\x84\x75\x38\x96\xe1\xd6" },
+      /* draft-nir-cfrg-chacha20-poly1305-03 */
+      { GCRY_CIPHER_CHACHA20,
+	"\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"
+	"\x90\x91\x92\x93\x94\x95\x96\x97\x98\x99\x9a\x9b\x9c\x9d\x9e\x9f",
+	"\x07\x00\x00\x00\x40\x41\x42\x43\x44\x45\x46\x47", 12,
+	"\x50\x51\x52\x53\xc0\xc1\xc2\xc3\xc4\xc5\xc6\xc7", 12,
+	"Ladies and Gentlemen of the class of '99: If I could offer you "
+	"only one tip for the future, sunscreen would be it.", 114,
+	"\xd3\x1a\x8d\x34\x64\x8e\x60\xdb\x7b\x86\xaf\xbc\x53\xef\x7e\xc2"
+	"\xa4\xad\xed\x51\x29\x6e\x08\xfe\xa9\xe2\xb5\xa7\x36\xee\x62\xd6"
+	"\x3d\xbe\xa4\x5e\x8c\xa9\x67\x12\x82\xfa\xfb\x69\xda\x92\x72\x8b"
+	"\x1a\x71\xde\x0a\x9e\x06\x0b\x29\x05\xd6\xa5\xb6\x7e\xcd\x3b\x36"
+	"\x92\xdd\xbd\x7f\x2d\x77\x8b\x8c\x98\x03\xae\xe3\x28\x09\x1b\x58"
+	"\xfa\xb3\x24\xe4\xfa\xd6\x75\x94\x55\x85\x80\x8b\x48\x31\xd7\xbc"
+	"\x3f\xf4\xde\xf0\x8e\x4b\x7a\x9d\xe5\x76\xd2\x65\x86\xce\xc6\x4b"
+	"\x61\x16",
+	"\x18\xfb\x11\xa5\x03\x1a\xd1\x3a\x7e\x3b\x03\xd4\x6e\xe3\xa6\xa7" }
+    };
+
+  gcry_cipher_hd_t hde, hdd;
+  unsigned char out[MAX_DATA_LEN];
+  unsigned char tag[16];
+  int i, keylen;
+  gcry_error_t err = 0;
+  size_t pos, poslen;
+  int byteNum;
+
+  if (verbose)
+    fprintf (stderr, "  Starting POLY1305 checks.\n");
+
+  for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++)
+    {
+      if (verbose)
+        fprintf (stderr, "    checking POLY1305 mode for %s [%i]\n",
+                 gcry_cipher_algo_name (tv[i].algo),
+                 tv[i].algo);
+      err = gcry_cipher_open (&hde, tv[i].algo, GCRY_CIPHER_MODE_POLY1305, 0);
+      if (!err)
+        err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_POLY1305, 0);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_open failed: %s\n", gpg_strerror (err));
+          return;
+        }
+
+      keylen = gcry_cipher_get_algo_keylen(tv[i].algo);
+      if (!keylen)
+        {
+          fail ("poly1305, gcry_cipher_get_algo_keylen failed\n");
+          return;
+        }
+
+      err = gcry_cipher_setkey (hde, tv[i].key, keylen);
+      if (!err)
+        err = gcry_cipher_setkey (hdd, tv[i].key, keylen);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_setkey failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      for (pos = 0; pos < tv[i].aadlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].aadlen) ? step : tv[i].aadlen - pos;
+
+          err = gcry_cipher_authenticate(hde, tv[i].aad + pos, poslen);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_authenticate (%d) (%d:%d) failed: "
+                    "%s\n", i, pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+          err = gcry_cipher_authenticate(hdd, tv[i].aad + pos, poslen);
+          if (err)
+            {
+              fail ("poly1305, de gcry_cipher_authenticate (%d) (%d:%d) failed: "
+	            "%s\n", i, pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      for (pos = 0; pos < tv[i].inlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+          err = gcry_cipher_encrypt (hde, out + pos, poslen,
+                                     tv[i].plaintext + pos, poslen);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_encrypt (%d) (%d:%d) failed: %s\n",
+                    i, pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].out, out, tv[i].inlen))
+        fail ("poly1305, encrypt mismatch entry %d (step %d)\n", i, step);
+
+      for (pos = 0; pos < tv[i].inlen; pos += step)
+        {
+          poslen = (pos + step < tv[i].inlen) ? step : tv[i].inlen - pos;
+
+          err = gcry_cipher_decrypt (hdd, out + pos, poslen, NULL, 0);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_decrypt (%d) (%d:%d) failed: %s\n",
+                    i, pos, step, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+        fail ("poly1305, decrypt mismatch entry %d (step %d)\n", i, step);
+
+      err = gcry_cipher_gettag (hde, out, 16);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_gettag(%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      if (memcmp (tv[i].tag, out, 16))
+        fail ("poly1305, encrypt tag mismatch entry %d\n", i);
+
+
+      err = gcry_cipher_checktag (hdd, out, 16);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_checktag(%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      err = gcry_cipher_reset(hde);
+      if (!err)
+        err = gcry_cipher_reset(hdd);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_reset (%d) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* gcry_cipher_reset clears the IV */
+      err = gcry_cipher_setiv (hde, tv[i].iv, tv[i].ivlen);
+      if (!err)
+        err = gcry_cipher_setiv (hdd, tv[i].iv, tv[i].ivlen);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_setiv failed: %s\n",
+                gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      /* this time we authenticate, encrypt and decrypt one byte at a time */
+      for (byteNum = 0; byteNum < tv[i].aadlen; ++byteNum)
+        {
+          err = gcry_cipher_authenticate(hde, tv[i].aad + byteNum, 1);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_authenticate (%d) (byte-buf) failed: "
+                    "%s\n", i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+          err = gcry_cipher_authenticate(hdd, tv[i].aad + byteNum, 1);
+          if (err)
+            {
+              fail ("poly1305, de gcry_cipher_authenticate (%d) (byte-buf) "
+	            "failed: %s\n", i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_encrypt (hde, out+byteNum, 1,
+                                     (tv[i].plaintext) + byteNum,
+                                     1);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_encrypt (%d) (byte-buf) failed: %s\n",
+                    i,  gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].out, out, tv[i].inlen))
+        fail ("poly1305, encrypt mismatch entry %d, (byte-buf)\n", i);
+
+      err = gcry_cipher_gettag (hde, tag, 16);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_gettag(%d) (byte-buf) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      if (memcmp (tv[i].tag, tag, 16))
+        fail ("poly1305, encrypt tag mismatch entry %d, (byte-buf)\n", i);
+
+      for (byteNum = 0; byteNum < tv[i].inlen; ++byteNum)
+        {
+          err = gcry_cipher_decrypt (hdd, out+byteNum, 1, NULL, 0);
+          if (err)
+            {
+              fail ("poly1305, gcry_cipher_decrypt (%d) (byte-buf) failed: %s\n",
+                    i, gpg_strerror (err));
+              gcry_cipher_close (hde);
+              gcry_cipher_close (hdd);
+              return;
+            }
+        }
+
+      if (memcmp (tv[i].plaintext, out, tv[i].inlen))
+        fail ("poly1305, decrypt mismatch entry %d\n", i);
+
+      err = gcry_cipher_checktag (hdd, tag, 16);
+      if (err)
+        {
+          fail ("poly1305, gcry_cipher_checktag(%d) (byte-buf) failed: %s\n",
+                i, gpg_strerror (err));
+          gcry_cipher_close (hde);
+          gcry_cipher_close (hdd);
+          return;
+        }
+
+      gcry_cipher_close (hde);
+      gcry_cipher_close (hdd);
+    }
+  if (verbose)
+    fprintf (stderr, "  Completed POLY1305 checks.\n");
+}
+
+
+static void
+check_poly1305_cipher (void)
+{
+  /* Large buffers, no splitting. */
+  _check_poly1305_cipher(0xffffffff);
+  /* Split input to one byte buffers. */
+  _check_poly1305_cipher(1);
+  /* Split input to 7 byte buffers. */
+  _check_poly1305_cipher(7);
+  /* Split input to 16 byte buffers. */
+  _check_poly1305_cipher(16);
+}
+
+
+static void
 check_ccm_cipher (void)
 {
 #ifdef HAVE_U64_TYPEDEF
@@ -4019,6 +4333,10 @@ check_ciphers (void)
 		 gcry_cipher_algo_name (algos2[i]));
 
       check_one_cipher (algos2[i], GCRY_CIPHER_MODE_STREAM, 0);
+      if (algos2[i] == GCRY_CIPHER_CHACHA20 ||
+	  algos2[i] == GCRY_CIPHER_SALSA20 ||
+	  algos2[i] == GCRY_CIPHER_SALSA20R12)
+	check_one_cipher (algos2[i], GCRY_CIPHER_MODE_POLY1305, 0);
     }
   /* we have now run all cipher's selftests */
 
@@ -4042,6 +4360,7 @@ check_cipher_modes(void)
   check_ofb_cipher ();
   check_ccm_cipher ();
   check_gcm_cipher ();
+  check_poly1305_cipher ();
   check_stream_cipher ();
   check_stream_cipher_large_block ();
 
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index a911ef8..7bf587f 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -907,15 +907,14 @@ static struct bench_ops ccm_authenticate_ops = {
 
 
 static void
-bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+bench_aead_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen,
+			     const char *nonce, size_t noncelen)
 {
   gcry_cipher_hd_t hd = obj->priv;
   int err;
   char tag[16];
-  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
-                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
 
-  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+  gcry_cipher_setiv (hd, nonce, noncelen);
 
   err = gcry_cipher_encrypt (hd, buf, buflen, buf, buflen);
   if (err)
@@ -937,15 +936,14 @@ bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 }
 
 static void
-bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
+bench_aead_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen,
+			     const char *nonce, size_t noncelen)
 {
   gcry_cipher_hd_t hd = obj->priv;
   int err;
   char tag[16] = { 0, };
-  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
-                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
 
-  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+  gcry_cipher_setiv (hd, nonce, noncelen);
 
   err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen);
   if (err)
@@ -969,17 +967,16 @@ bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen)
 }
 
 static void
-bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
-           size_t buflen)
+bench_aead_authenticate_do_bench (struct bench_obj *obj, void *buf,
+				  size_t buflen, const char *nonce,
+				  size_t noncelen)
 {
   gcry_cipher_hd_t hd = obj->priv;
   int err;
   char tag[16] = { 0, };
-  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
-                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
   char data = 0xff;
 
-  gcry_cipher_setiv (hd, nonce, sizeof (nonce));
+  gcry_cipher_setiv (hd, nonce, noncelen);
 
   err = gcry_cipher_authenticate (hd, buf, buflen);
   if (err)
@@ -1009,6 +1006,34 @@ bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
     }
 }
 
+
+static void
+bench_gcm_encrypt_do_bench (struct bench_obj *obj, void *buf,
+			    size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+  bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_gcm_decrypt_do_bench (struct bench_obj *obj, void *buf,
+			    size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+  bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_gcm_authenticate_do_bench (struct bench_obj *obj, void *buf,
+				 size_t buflen)
+{
+  char nonce[12] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce,
+                     0xdb, 0xad, 0xde, 0xca, 0xf8, 0x88, };
+  bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
 static struct bench_ops gcm_encrypt_ops = {
   &bench_encrypt_init,
   &bench_encrypt_free,
@@ -1028,6 +1053,49 @@ static struct bench_ops gcm_authenticate_ops = {
 };
 
 
+static void
+bench_poly1305_encrypt_do_bench (struct bench_obj *obj, void *buf,
+				 size_t buflen)
+{
+  char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
+  bench_aead_encrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_poly1305_decrypt_do_bench (struct bench_obj *obj, void *buf,
+				 size_t buflen)
+{
+  char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
+  bench_aead_decrypt_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static void
+bench_poly1305_authenticate_do_bench (struct bench_obj *obj, void *buf,
+				      size_t buflen)
+{
+  char nonce[8] = { 0xca, 0xfe, 0xba, 0xbe, 0xfa, 0xce, 0xdb, 0xad };
+  bench_aead_authenticate_do_bench (obj, buf, buflen, nonce, sizeof(nonce));
+}
+
+static struct bench_ops poly1305_encrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_poly1305_encrypt_do_bench
+};
+
+static struct bench_ops poly1305_decrypt_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_poly1305_decrypt_do_bench
+};
+
+static struct bench_ops poly1305_authenticate_ops = {
+  &bench_encrypt_init,
+  &bench_encrypt_free,
+  &bench_poly1305_authenticate_do_bench
+};
+
+
 static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_ECB, "ECB enc", &encrypt_ops},
   {GCRY_CIPHER_MODE_ECB, "ECB dec", &decrypt_ops},
@@ -1047,6 +1115,9 @@ static struct bench_cipher_mode cipher_modes[] = {
   {GCRY_CIPHER_MODE_GCM, "GCM enc", &gcm_encrypt_ops},
   {GCRY_CIPHER_MODE_GCM, "GCM dec", &gcm_decrypt_ops},
   {GCRY_CIPHER_MODE_GCM, "GCM auth", &gcm_authenticate_ops},
+  {GCRY_CIPHER_MODE_POLY1305, "POLY1305 enc", &poly1305_encrypt_ops},
+  {GCRY_CIPHER_MODE_POLY1305, "POLY1305 dec", &poly1305_decrypt_ops},
+  {GCRY_CIPHER_MODE_POLY1305, "POLY1305 auth", &poly1305_authenticate_ops},
   {0},
 };
 
@@ -1066,8 +1137,9 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
   if (!blklen)
     return;
 
-  /* Stream cipher? Only test with ECB. */
-  if (blklen == 1 && mode.mode != GCRY_CIPHER_MODE_ECB)
+  /* Stream cipher? Only test with "ECB" and POLY1305. */
+  if (blklen == 1 && (mode.mode != GCRY_CIPHER_MODE_ECB &&
+		      mode.mode != GCRY_CIPHER_MODE_POLY1305))
     return;
   if (blklen == 1 && mode.mode == GCRY_CIPHER_MODE_ECB)
     {
@@ -1075,6 +1147,12 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode)
       mode.name = mode.ops == &encrypt_ops ? "STREAM enc" : "STREAM dec";
     }
 
+  /* Poly1305 has restrictions for cipher algorithm */
+  if (mode.mode == GCRY_CIPHER_MODE_POLY1305 &&
+      (algo != GCRY_CIPHER_SALSA20 && algo != GCRY_CIPHER_SALSA20R12 &&
+       algo != GCRY_CIPHER_CHACHA20))
+    return;
+
   /* CCM has restrictions for block-size */
   if (mode.mode == GCRY_CIPHER_MODE_CCM && blklen != GCRY_CCM_BLOCK_LEN)
     return;

commit 73b3b75c2221a6e3bed4117e0a206a1193acd2ed
Author: Jussi Kivilinna <jussi.kivilinna at iki.fi>
Date:   Sun May 11 12:00:19 2014 +0300

    Add Poly1305-AES (-Camellia, etc) MACs
    
    * cipher/mac-internal.h (_gcry_mac_type_spec_poly1305_aes)
    (_gcry_mac_type_spec_poly1305_camellia)
    (_gcry_mac_type_spec_poly1305_twofish)
    (_gcry_mac_type_spec_poly1305_serpent)
    (_gcry_mac_type_spec_poly1305_seed): New.
    * cipher/mac-poly1305.c (poly1305mac_context_s): Add 'hd' and
    'nonce_set'.
    (poly1305mac_open, poly1305mac_close, poly1305mac_setkey): Add handling
    for Poly1305-*** MACs.
    (poly1305mac_prepare_key, poly1305mac_setiv): New.
    (poly1305mac_reset, poly1305mac_write, poly1305mac_read): Add handling
    for 'nonce_set'.
    (poly1305mac_ops): Add 'poly1305mac_setiv'.
    (_gcry_mac_type_spec_poly1305_aes)
    (_gcry_mac_type_spec_poly1305_camellia)
    (_gcry_mac_type_spec_poly1305_twofish)
    (_gcry_mac_type_spec_poly1305_serpent)
    (_gcry_mac_type_spec_poly1305_seed): New.
    * cipher/mac.c (mac_list): Add Poly1305-AES, Poly1305-Twofish,
    Poly1305-Serpent, Poly1305-SEED and Poly1305-Camellia.
    * src/gcrypt.h.in: Add 'GCRY_MAC_POLY1305_AES',
    'GCRY_MAC_POLY1305_CAMELLIA', 'GCRY_MAC_POLY1305_TWOFISH',
    'GCRY_MAC_POLY1305_SERPENT' and 'GCRY_MAC_POLY1305_SEED'.
    * tests/basic.c (check_mac): Add Poly1305-AES test vectors.
    * tests/bench-slope.c (bench_mac_init): Set IV for Poly1305-*** MACs.
    * tests/bench-slope.c (mac_bench): Set IV for Poly1305-*** MACs.
    --
    
    Patch adds Bernstein's Poly1305-AES message authentication code to libgcrypt
    and other variants of Poly1305-<128-bit block cipher>.
    
    Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>

diff --git a/cipher/mac-internal.h b/cipher/mac-internal.h
index 81b6185..f65a8ae 100644
--- a/cipher/mac-internal.h
+++ b/cipher/mac-internal.h
@@ -217,3 +217,18 @@ extern gcry_mac_spec_t _gcry_mac_type_spec_gmac_camellia;
  * The Poly1305 MAC algorithm specifications (mac-poly1305.c).
  */
 extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac;
+#if USE_AES
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_aes;
+#endif
+#if USE_CAMELLIA
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_camellia;
+#endif
+#if USE_TWOFISH
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_twofish;
+#endif
+#if USE_SERPENT
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_serpent;
+#endif
+#if USE_SEED
+extern gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_seed;
+#endif
diff --git a/cipher/mac-poly1305.c b/cipher/mac-poly1305.c
index e265b64..76b369a 100644
--- a/cipher/mac-poly1305.c
+++ b/cipher/mac-poly1305.c
@@ -30,8 +30,10 @@
 
 struct poly1305mac_context_s {
   poly1305_context_t ctx;
+  gcry_cipher_hd_t hd;
   struct {
     unsigned int key_set:1;
+    unsigned int nonce_set:1;
     unsigned int tag:1;
   } marks;
   byte tag[POLY1305_TAGLEN];
@@ -44,6 +46,9 @@ poly1305mac_open (gcry_mac_hd_t h)
 {
   struct poly1305mac_context_s *mac_ctx;
   int secure = (h->magic == CTX_MAGIC_SECURE);
+  unsigned int flags = (secure ? GCRY_CIPHER_SECURE : 0);
+  gcry_err_code_t err;
+  int cipher_algo;
 
   if (secure)
     mac_ctx = xtrycalloc_secure (1, sizeof(*mac_ctx));
@@ -55,14 +60,71 @@ poly1305mac_open (gcry_mac_hd_t h)
 
   h->u.poly1305mac.ctx = mac_ctx;
 
+  switch (h->spec->algo)
+    {
+    default:
+      /* already checked. */
+    case GCRY_MAC_POLY1305:
+      /* plain Poly1305. */
+      cipher_algo = -1;
+      return 0;
+    case GCRY_MAC_POLY1305_AES:
+      cipher_algo = GCRY_CIPHER_AES;
+      break;
+    case GCRY_MAC_POLY1305_CAMELLIA:
+      cipher_algo = GCRY_CIPHER_CAMELLIA128;
+      break;
+    case GCRY_MAC_POLY1305_TWOFISH:
+      cipher_algo = GCRY_CIPHER_TWOFISH;
+      break;
+    case GCRY_MAC_POLY1305_SERPENT:
+      cipher_algo = GCRY_CIPHER_SERPENT128;
+      break;
+    case GCRY_MAC_POLY1305_SEED:
+      cipher_algo = GCRY_CIPHER_SEED;
+      break;
+    }
+
+  err = _gcry_cipher_open_internal (&mac_ctx->hd, cipher_algo,
+				    GCRY_CIPHER_MODE_ECB, flags);
+  if (err)
+    goto err_free;
+
   return 0;
+
+err_free:
+  xfree(h->u.poly1305mac.ctx);
+  return err;
 }
 
 
 static void
 poly1305mac_close (gcry_mac_hd_t h)
 {
-  xfree(h->u.poly1305mac.ctx);
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+
+  if (h->spec->algo != GCRY_MAC_POLY1305)
+    _gcry_cipher_close (mac_ctx->hd);
+
+  xfree(mac_ctx);
+}
+
+
+static gcry_err_code_t
+poly1305mac_prepare_key (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  size_t block_keylen = keylen - 16;
+
+  /* Need at least 16 + 1 byte key. */
+  if (keylen <= 16)
+    return GPG_ERR_INV_KEYLEN;
+
+  /* For Poly1305-AES, first part of key is passed to Poly1305 as is. */
+  memcpy (mac_ctx->key, key + block_keylen, 16);
+
+  /* Remaining part is used as key for the block cipher. */
+  return _gcry_cipher_setkey (mac_ctx->hd, key, block_keylen);
 }
 
 
@@ -77,22 +139,74 @@ poly1305mac_setkey (gcry_mac_hd_t h, const unsigned char *key, size_t keylen)
   memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
 
   mac_ctx->marks.key_set = 0;
+  mac_ctx->marks.nonce_set = 0;
   mac_ctx->marks.tag = 0;
 
-  if (keylen != POLY1305_KEYLEN)
-    return GPG_ERR_INV_KEYLEN;
-
-  memcpy(mac_ctx->key, key, POLY1305_KEYLEN);
+  if (h->spec->algo != GCRY_MAC_POLY1305)
+    {
+      err = poly1305mac_prepare_key (h, key, keylen);
+      if (err)
+        return err;
 
-  err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
-  if (err)
+      /* Poly1305-AES/etc also need nonce. */
+      mac_ctx->marks.key_set = 1;
+      mac_ctx->marks.nonce_set = 0;
+    }
+  else
     {
-      memset(&mac_ctx->key, 0, sizeof(mac_ctx->key));
-      return err;
+      /* For plain Poly1305, key is the nonce and setup is complete now. */
+
+      if (keylen != POLY1305_KEYLEN)
+        return GPG_ERR_INV_KEYLEN;
+
+      memcpy (mac_ctx->key, key, 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;
+      mac_ctx->marks.nonce_set = 1;
     }
 
-  mac_ctx->marks.key_set = 1;
+  return 0;
+}
+
+
+static gcry_err_code_t
+poly1305mac_setiv (gcry_mac_hd_t h, const unsigned char *iv, size_t ivlen)
+{
+  struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
+  gcry_err_code_t err;
+
+  if (h->spec->algo == GCRY_MAC_POLY1305)
+    return GPG_ERR_INV_ARG;
+
+  if (ivlen != 16)
+    return GPG_ERR_INV_ARG;
 
+  if (!mac_ctx->marks.key_set)
+    return 0;
+
+  memset(&mac_ctx->ctx, 0, sizeof(mac_ctx->ctx));
+  memset(&mac_ctx->tag, 0, sizeof(mac_ctx->tag));
+  mac_ctx->marks.nonce_set = 0;
+  mac_ctx->marks.tag = 0;
+
+  /* Prepare second part of the poly1305 key. */
+
+  err = _gcry_cipher_encrypt (mac_ctx->hd, mac_ctx->key + 16, 16, iv, 16);
+  if (err)
+    return err;
+
+  err = _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
+  if (err)
+    return err;
+
+  mac_ctx->marks.nonce_set = 1;
   return 0;
 }
 
@@ -102,13 +216,14 @@ poly1305mac_reset (gcry_mac_hd_t h)
 {
   struct poly1305mac_context_s *mac_ctx = h->u.poly1305mac.ctx;
 
-  if (!mac_ctx->marks.key_set)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_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.nonce_set = 1;
   mac_ctx->marks.tag = 0;
 
   return _gcry_poly1305_init (&mac_ctx->ctx, mac_ctx->key, POLY1305_KEYLEN);
@@ -120,7 +235,8 @@ 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)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set ||
+      mac_ctx->marks.tag)
     return GPG_ERR_INV_STATE;
 
   _gcry_poly1305_update (&mac_ctx->ctx, buf, buflen);
@@ -133,7 +249,7 @@ 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)
+  if (!mac_ctx->marks.key_set || !mac_ctx->marks.nonce_set)
     return GPG_ERR_INV_STATE;
 
   if (!mac_ctx->marks.tag)
@@ -197,7 +313,7 @@ static gcry_mac_spec_ops_t poly1305mac_ops = {
   poly1305mac_open,
   poly1305mac_close,
   poly1305mac_setkey,
-  NULL,
+  poly1305mac_setiv,
   poly1305mac_reset,
   poly1305mac_write,
   poly1305mac_read,
@@ -211,3 +327,33 @@ gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac = {
   GCRY_MAC_POLY1305, {0, 0}, "POLY1305",
   &poly1305mac_ops
 };
+#if USE_AES
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_aes = {
+  GCRY_MAC_POLY1305_AES, {0, 0}, "POLY1305_AES",
+  &poly1305mac_ops
+};
+#endif
+#if USE_CAMELLIA
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_camellia = {
+  GCRY_MAC_POLY1305_CAMELLIA, {0, 0}, "POLY1305_CAMELLIA",
+  &poly1305mac_ops
+};
+#endif
+#if USE_TWOFISH
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_twofish = {
+  GCRY_MAC_POLY1305_TWOFISH, {0, 0}, "POLY1305_TWOFISH",
+  &poly1305mac_ops
+};
+#endif
+#if USE_SERPENT
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_serpent = {
+  GCRY_MAC_POLY1305_SERPENT, {0, 0}, "POLY1305_SERPENT",
+  &poly1305mac_ops
+};
+#endif
+#if USE_SEED
+gcry_mac_spec_t _gcry_mac_type_spec_poly1305mac_seed = {
+  GCRY_MAC_POLY1305_SEED, {0, 0}, "POLY1305_SEED",
+  &poly1305mac_ops
+};
+#endif
diff --git a/cipher/mac.c b/cipher/mac.c
index e583369..30117b9 100644
--- a/cipher/mac.c
+++ b/cipher/mac.c
@@ -75,14 +75,17 @@ static gcry_mac_spec_t *mac_list[] = {
 #if USE_AES
   &_gcry_mac_type_spec_cmac_aes,
   &_gcry_mac_type_spec_gmac_aes,
+  &_gcry_mac_type_spec_poly1305mac_aes,
 #endif
 #if USE_TWOFISH
   &_gcry_mac_type_spec_cmac_twofish,
   &_gcry_mac_type_spec_gmac_twofish,
+  &_gcry_mac_type_spec_poly1305mac_twofish,
 #endif
 #if USE_SERPENT
   &_gcry_mac_type_spec_cmac_serpent,
   &_gcry_mac_type_spec_gmac_serpent,
+  &_gcry_mac_type_spec_poly1305mac_serpent,
 #endif
 #if USE_RFC2268
   &_gcry_mac_type_spec_cmac_rfc2268,
@@ -90,10 +93,12 @@ static gcry_mac_spec_t *mac_list[] = {
 #if USE_SEED
   &_gcry_mac_type_spec_cmac_seed,
   &_gcry_mac_type_spec_gmac_seed,
+  &_gcry_mac_type_spec_poly1305mac_seed,
 #endif
 #if USE_CAMELLIA
   &_gcry_mac_type_spec_cmac_camellia,
   &_gcry_mac_type_spec_gmac_camellia,
+  &_gcry_mac_type_spec_poly1305mac_camellia,
 #endif
 #ifdef USE_IDEA
   &_gcry_mac_type_spec_cmac_idea,
diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in
index 8648e96..3145020 100644
--- a/src/gcrypt.h.in
+++ b/src/gcrypt.h.in
@@ -1343,7 +1343,12 @@ enum gcry_mac_algos
     GCRY_MAC_GMAC_SERPENT       = 404,
     GCRY_MAC_GMAC_SEED          = 405,
 
-    GCRY_MAC_POLY1305           = 501
+    GCRY_MAC_POLY1305           = 501,
+    GCRY_MAC_POLY1305_AES       = 502,
+    GCRY_MAC_POLY1305_CAMELLIA  = 503,
+    GCRY_MAC_POLY1305_TWOFISH   = 504,
+    GCRY_MAC_POLY1305_SERPENT   = 505,
+    GCRY_MAC_POLY1305_SEED      = 506
   };
 
 /* Flags used with the open function.  */
diff --git a/tests/basic.c b/tests/basic.c
index cc0f4c1..de10617 100644
--- a/tests/basic.c
+++ b/tests/basic.c
@@ -5596,6 +5596,39 @@ check_mac (void)
         "\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" },
+      /* from http://cr.yp.to/mac/poly1305-20050329.pdf */
+      { GCRY_MAC_POLY1305_AES,
+        "\xf3\xf6",
+        "\xec\x07\x4c\x83\x55\x80\x74\x17\x01\x42\x5b\x62\x32\x35\xad\xd6"
+        "\x85\x1f\xc4\x0c\x34\x67\xac\x0b\xe0\x5c\xc2\x04\x04\xf3\xf7\x00",
+        "\xf4\xc6\x33\xc3\x04\x4f\xc1\x45\xf8\x4f\x33\x5c\xb8\x19\x53\xde",
+        "\xfb\x44\x73\x50\xc4\xe8\x68\xc5\x2a\xc3\x27\x5c\xf9\xd4\x32\x7e",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "",
+        "\x75\xde\xaa\x25\xc0\x9f\x20\x8e\x1d\xc4\xce\x6b\x5c\xad\x3f\xbf"
+        "\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",
+        "\x61\xee\x09\x21\x8d\x29\xb0\xaa\xed\x7e\x15\x4a\x2c\x55\x09\xcc",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "\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",
+        "\x6a\xcb\x5f\x61\xa7\x17\x6d\xd3\x20\xc5\xc1\xeb\x2e\xdc\xdc\x74"
+        "\x48\x44\x3d\x0b\xb0\xd2\x11\x09\xc8\x9a\x10\x0b\x5c\xe2\xc2\x08",
+        "\x0e\xe1\xc1\x6b\xb7\x3f\x0f\x4f\xd1\x98\x81\x75\x3c\x01\xcd\xbe",
+        "\xae\x21\x2a\x55\x39\x97\x29\x59\x5d\xea\x45\x8b\xc6\x21\xff\x0e",
+        0, 32 },
+      { GCRY_MAC_POLY1305_AES,
+        "\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",
+        "\xe1\xa5\x66\x8a\x4d\x5b\x66\xa5\xf6\x8c\xc5\x42\x4e\xd5\x98\x2d"
+        "\x12\x97\x6a\x08\xc4\x42\x6d\x0c\xe8\xa8\x24\x07\xc4\xf4\x82\x07",
+        "\x51\x54\xad\x0d\x2c\xb2\x6e\x01\x27\x4f\xc5\x11\x48\x49\x1f\x1b",
+	"\x9a\xe8\x31\xe7\x43\x97\x8d\x3a\x23\x52\x7c\x71\x28\x14\x9e\x3a",
+        0, 32 },
       { 0 },
     };
   int i;
diff --git a/tests/bench-slope.c b/tests/bench-slope.c
index 3d8ae37..a911ef8 100644
--- a/tests/bench-slope.c
+++ b/tests/bench-slope.c
@@ -1316,6 +1316,19 @@ bench_mac_init (struct bench_obj *obj)
       exit (1);
     }
 
+  switch (mode->algo)
+    {
+    default:
+      break;
+    case GCRY_MAC_POLY1305_AES:
+    case GCRY_MAC_POLY1305_CAMELLIA:
+    case GCRY_MAC_POLY1305_TWOFISH:
+    case GCRY_MAC_POLY1305_SERPENT:
+    case GCRY_MAC_POLY1305_SEED:
+      gcry_mac_setiv (hd, key, 16);
+      break;
+    }
+
   obj->priv = hd;
 
   return 0;
diff --git a/tests/benchmark.c b/tests/benchmark.c
index 9fd716d..042e721 100644
--- a/tests/benchmark.c
+++ b/tests/benchmark.c
@@ -509,6 +509,18 @@ mac_bench ( const char *algoname )
   for (i=0; i < bufsize; i++)
     buf[i] = i;
 
+  if (algo >= GCRY_MAC_POLY1305_AES && algo <= GCRY_MAC_POLY1305_SEED)
+    {
+      static const char iv[16] = { 1, 2, 3, 4, };
+      err = gcry_mac_setiv(hd, iv, sizeof(iv));
+      if (err)
+        {
+          fprintf (stderr, PGM ": error setting nonce for mac algorithm `%s': %s\n",
+                   algoname, gpg_strerror (err));
+          exit (1);
+        }
+    }
+
   printf ("%-20s", gcry_mac_algo_name (algo));
 
   start_timer ();

commit b8794fed68ebe7567f4617141f0996ad290d9120
Author: Jussi Kivilinna <jussi.kivilinna at iki.fi>
Date:   Sun May 11 12:00:19 2014 +0300

    Add Poly1305 MAC
    
    * 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>

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..d2c6b5c
--- /dev/null
+++ b/cipher/poly1305-internal.h
@@ -0,0 +1,93 @@
+/* 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
+
+/* State alignment for default implementation. */
+#define POLY1305_REF_ALIGNMENT sizeof(void *)
+
+
+/* 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 POLY1305_REF_ALIGNMENT
+
+
+typedef struct poly1305_key_s
+{
+  byte b[POLY1305_KEYLEN];
+} poly1305_key_t;
+
+
+typedef struct poly1305_ops_s
+{
+  size_t block_size;
+  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];
+  byte buffer[POLY1305_LARGEST_BLOCKSIZE];
+  const poly1305_ops_t *ops;
+  unsigned int leftover;
+} 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..472ae42
--- /dev/null
+++ b/cipher/poly1305.c
@@ -0,0 +1,766 @@
+/* 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 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_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 */
+
+
+
+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->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;
+  size_t block_size = ctx->ops->block_size;
+
+  /* handle leftover */
+  if (ctx->leftover)
+    {
+      size_t want = (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 < block_size)
+	return;
+      burn = ctx->ops->blocks (state, ctx->buffer, block_size);
+      ctx->leftover = 0;
+    }
+
+  /* process full blocks */
+  if (bytes >= block_size)
+    {
+      size_t want = (bytes & ~(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) )

-----------------------------------------------------------------------

Summary of changes:
 cipher/Makefile.am         |    5 +-
 cipher/cipher-internal.h   |   38 +++
 cipher/cipher-poly1305.c   |  296 +++++++++++++++++
 cipher/cipher.c            |   51 ++-
 cipher/mac-internal.h      |   32 +-
 cipher/mac-poly1305.c      |  359 +++++++++++++++++++++
 cipher/mac.c               |    6 +
 cipher/poly1305-internal.h |   93 ++++++
 cipher/poly1305.c          |  766 ++++++++++++++++++++++++++++++++++++++++++++
 src/gcrypt.h.in            |   30 +-
 tests/basic.c              |  472 ++++++++++++++++++++++++++-
 tests/bench-slope.c        |  123 ++++++-
 tests/benchmark.c          |   14 +-
 13 files changed, 2244 insertions(+), 41 deletions(-)
 create mode 100644 cipher/cipher-poly1305.c
 create mode 100644 cipher/mac-poly1305.c
 create mode 100644 cipher/poly1305-internal.h
 create mode 100644 cipher/poly1305.c


hooks/post-receive
-- 
The GNU crypto library
http://git.gnupg.org


_______________________________________________
Gnupg-commits mailing list
Gnupg-commits at gnupg.org
http://lists.gnupg.org/mailman/listinfo/gnupg-commits




More information about the Gcrypt-devel mailing list