summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@iki.fi>2014-05-11 12:00:19 +0300
committerJussi Kivilinna <jussi.kivilinna@iki.fi>2014-05-12 20:32:45 +0300
commite813958419b0ec4439e6caf07d3b2234cffa2bfa (patch)
tree6d50e973040b0f4efbd90ad94c0f4efd81d8d266
parent73b3b75c2221a6e3bed4117e0a206a1193acd2ed (diff)
downloadlibgcrypt-e813958419b0ec4439e6caf07d3b2234cffa2bfa.tar.gz
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@iki.fi>
-rw-r--r--cipher/Makefile.am2
-rw-r--r--cipher/cipher-internal.h38
-rw-r--r--cipher/cipher-poly1305.c296
-rw-r--r--cipher/cipher.c51
-rw-r--r--src/gcrypt.h.in21
-rw-r--r--tests/basic.c319
-rw-r--r--tests/bench-slope.c108
7 files changed, 805 insertions, 30 deletions
diff --git a/cipher/Makefile.am b/cipher/Makefile.am
index a8b86e6b..44686470 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 cdac445f..f6bda668 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 00000000..a22ffa37
--- /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@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 47513021..da590610 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 31450203..bd38a248 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 de106175..97409193 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 a911ef85..7bf587fe 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;