[PATCH 3/3] Add Poly1305 based cipher AEAD mode

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


* 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>
---
 cipher/Makefile.am       |    2 
 cipher/cipher-internal.h |   38 +++++
 cipher/cipher-poly1305.c |  296 +++++++++++++++++++++++++++++++++++++++++++
 cipher/cipher.c          |   51 +++++++
 src/gcrypt.h.in          |   21 ++-
 tests/basic.c            |  319 ++++++++++++++++++++++++++++++++++++++++++++++
 tests/bench-slope.c      |  108 +++++++++++++---
 7 files changed, 805 insertions(+), 30 deletions(-)
 create mode 100644 cipher/cipher-poly1305.c

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;




More information about the Gcrypt-devel mailing list