From e813958419b0ec4439e6caf07d3b2234cffa2bfa Mon Sep 17 00:00:00 2001 From: Jussi Kivilinna Date: Sun, 11 May 2014 12:00:19 +0300 Subject: 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 --- 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 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 + * + * 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 . + */ + +#include +#include +#include +#include +#include + +#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 @@ -1619,6 +1619,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) { @@ -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; -- cgit v1.2.1