diff options
-rw-r--r-- | cipher/Makefile.am | 1 | ||||
-rw-r--r-- | cipher/cipher-ctr.c | 21 | ||||
-rw-r--r-- | cipher/cipher-internal.h | 57 | ||||
-rw-r--r-- | cipher/cipher-ocb.c | 3 | ||||
-rw-r--r-- | cipher/cipher-siv.c | 377 | ||||
-rw-r--r-- | cipher/cipher.c | 93 | ||||
-rw-r--r-- | doc/gcrypt.texi | 37 | ||||
-rw-r--r-- | src/gcrypt.h.in | 14 | ||||
-rw-r--r-- | tests/basic.c | 421 | ||||
-rw-r--r-- | tests/bench-slope.c | 78 |
10 files changed, 1082 insertions, 20 deletions
diff --git a/cipher/Makefile.am b/cipher/Makefile.am index 52a00aa9..4d3e0d19 100644 --- a/cipher/Makefile.am +++ b/cipher/Makefile.am @@ -53,6 +53,7 @@ libcipher_la_SOURCES = \ cipher-ocb.c \ cipher-xts.c \ cipher-eax.c \ + cipher-siv.c \ cipher-selftest.c cipher-selftest.h \ pubkey.c pubkey-internal.h pubkey-util.c \ md.c \ diff --git a/cipher/cipher-ctr.c b/cipher/cipher-ctr.c index 5f0afc2f..d66c5687 100644 --- a/cipher/cipher-ctr.c +++ b/cipher/cipher-ctr.c @@ -31,9 +31,10 @@ gcry_err_code_t -_gcry_cipher_ctr_encrypt (gcry_cipher_hd_t c, - unsigned char *outbuf, size_t outbuflen, - const unsigned char *inbuf, size_t inbuflen) +_gcry_cipher_ctr_encrypt_ctx (gcry_cipher_hd_t c, + unsigned char *outbuf, size_t outbuflen, + const unsigned char *inbuf, size_t inbuflen, + void *algo_context) { size_t n; int i; @@ -65,7 +66,7 @@ _gcry_cipher_ctr_encrypt (gcry_cipher_hd_t c, nblocks = inbuflen >> blocksize_shift; if (nblocks && c->bulk.ctr_enc) { - c->bulk.ctr_enc (&c->context.c, c->u_ctr.ctr, outbuf, inbuf, nblocks); + c->bulk.ctr_enc (algo_context, c->u_ctr.ctr, outbuf, inbuf, nblocks); inbuf += nblocks << blocksize_shift; outbuf += nblocks << blocksize_shift; inbuflen -= nblocks << blocksize_shift; @@ -80,7 +81,7 @@ _gcry_cipher_ctr_encrypt (gcry_cipher_hd_t c, n = blocksize; do { - nburn = enc_fn (&c->context.c, tmp, c->u_ctr.ctr); + nburn = enc_fn (algo_context, tmp, c->u_ctr.ctr); burn = nburn > burn ? nburn : burn; cipher_block_add(c->u_ctr.ctr, 1, blocksize); @@ -118,3 +119,13 @@ _gcry_cipher_ctr_encrypt (gcry_cipher_hd_t c, return 0; } + + +gcry_err_code_t +_gcry_cipher_ctr_encrypt (gcry_cipher_hd_t c, + unsigned char *outbuf, size_t outbuflen, + const unsigned char *inbuf, size_t inbuflen) +{ + return _gcry_cipher_ctr_encrypt_ctx (c, outbuf, outbuflen, inbuf, inbuflen, + &c->context.c); +} diff --git a/cipher/cipher-internal.h b/cipher/cipher-internal.h index 0e4a90fc..e9f48a2f 100644 --- a/cipher/cipher-internal.h +++ b/cipher/cipher-internal.h @@ -398,6 +398,31 @@ struct gcry_cipher_handle * cipher context. */ char *tweak_context; } xts; + + /* Mode specific storage for SIV mode. */ + struct { + /* Tag used for decryption. */ + unsigned char dec_tag[GCRY_SIV_BLOCK_LEN]; + + /* S2V state. */ + unsigned char s2v_d[GCRY_SIV_BLOCK_LEN]; + + /* Number of AAD elements processed. */ + unsigned int aad_count:8; + + /* Flags for SIV state. */ + unsigned int dec_tag_set:1; + + /* --- Following members are not cleared in gcry_cipher_reset --- */ + + /* S2V CMAC state. */ + gcry_cmac_context_t s2v_cmac; + unsigned char s2v_zero_block[GCRY_SIV_BLOCK_LEN]; + + /* Pointer to CTR cipher context, allocated after actual + * cipher context. */ + char *ctr_context; + } siv; } u_mode; /* What follows are two contexts of the cipher in use. The first @@ -453,6 +478,11 @@ gcry_err_code_t _gcry_cipher_ofb_encrypt const unsigned char *inbuf, size_t inbuflen); /*-- cipher-ctr.c --*/ +gcry_err_code_t _gcry_cipher_ctr_encrypt_ctx +/* */ (gcry_cipher_hd_t c, + unsigned char *outbuf, size_t outbuflen, + const unsigned char *inbuf, size_t inbuflen, + void *algo_context); gcry_err_code_t _gcry_cipher_ctr_encrypt /* */ (gcry_cipher_hd_t c, unsigned char *outbuf, size_t outbuflen, @@ -622,6 +652,33 @@ gcry_err_code_t _gcry_cipher_xts_decrypt const unsigned char *inbuf, size_t inbuflen); +/*-- cipher-siv.c --*/ +gcry_err_code_t _gcry_cipher_siv_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_siv_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_siv_set_nonce +/* */ (gcry_cipher_hd_t c, const unsigned char *nonce, + size_t noncelen); +gcry_err_code_t _gcry_cipher_siv_authenticate +/* */ (gcry_cipher_hd_t c, const unsigned char *abuf, size_t abuflen); +gcry_err_code_t _gcry_cipher_siv_set_decryption_tag +/* */ (gcry_cipher_hd_t c, const byte *tag, size_t taglen); +gcry_err_code_t _gcry_cipher_siv_get_tag +/* */ (gcry_cipher_hd_t c, + unsigned char *outtag, size_t taglen); +gcry_err_code_t _gcry_cipher_siv_check_tag +/* */ (gcry_cipher_hd_t c, + const unsigned char *intag, size_t taglen); +gcry_err_code_t _gcry_cipher_siv_setkey +/* */ (gcry_cipher_hd_t c, + const unsigned char *ctrkey, size_t ctrkeylen); + + /* Return the L-value for block N. Note: 'cipher_ocb.c' ensures that N * will never be multiple of 65536 (1 << OCB_L_TABLE_SIZE), thus N can * be directly passed to _gcry_ctz() function and resulting index will diff --git a/cipher/cipher-ocb.c b/cipher/cipher-ocb.c index 24db6a9e..bfafa4c8 100644 --- a/cipher/cipher-ocb.c +++ b/cipher/cipher-ocb.c @@ -107,7 +107,8 @@ ocb_get_L_big (gcry_cipher_hd_t c, u64 n, unsigned char *l_buf) /* Called after key has been set. Sets up L table. */ -void _gcry_cipher_ocb_setkey (gcry_cipher_hd_t c) +void +_gcry_cipher_ocb_setkey (gcry_cipher_hd_t c) { unsigned char ktop[OCB_BLOCK_LEN]; unsigned int burn = 0; diff --git a/cipher/cipher-siv.c b/cipher/cipher-siv.c new file mode 100644 index 00000000..9a71f2ef --- /dev/null +++ b/cipher/cipher-siv.c @@ -0,0 +1,377 @@ +/* cipher-siv.c - SIV implementation + * Copyright (C) 2021 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" + + +static inline void +s2v_double (unsigned char *d) +{ + u64 hi, lo, mask; + + hi = buf_get_be64(d + 0); + lo = buf_get_be64(d + 8); + + mask = -(hi >> 63); + hi = (hi << 1) ^ (lo >> 63); + lo = (lo << 1) ^ (mask & 0x87); + + buf_put_be64(d + 0, hi); + buf_put_be64(d + 8, lo); +} + + +static void +s2v_pad (unsigned char *out, const byte *in, size_t inlen) +{ + static const unsigned char padding[GCRY_SIV_BLOCK_LEN] = { 0x80 }; + + gcry_assert(inlen < GCRY_SIV_BLOCK_LEN); + + buf_cpy (out, in, inlen); + buf_cpy (out + inlen, padding, GCRY_SIV_BLOCK_LEN - inlen); +} + + +gcry_err_code_t +_gcry_cipher_siv_setkey (gcry_cipher_hd_t c, + const unsigned char *ctrkey, size_t ctrkeylen) +{ + static const unsigned char zero[GCRY_SIV_BLOCK_LEN] = { 0 }; + gcry_err_code_t err; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + + c->u_mode.siv.aad_count = 0; + c->u_mode.siv.dec_tag_set = 0; + c->marks.tag = 0; + c->marks.iv = 0; + + /* Set CTR mode key. */ + err = c->spec->setkey (c->u_mode.siv.ctr_context, ctrkey, ctrkeylen, + &c->bulk); + if (err != 0) + return err; + + /* Initialize S2V. */ + memset (&c->u_mode.siv.s2v_cmac, 0, sizeof(c->u_mode.siv.s2v_cmac)); + err = _gcry_cmac_generate_subkeys (c, &c->u_mode.siv.s2v_cmac); + if (err != 0) + return err; + + err = _gcry_cmac_write (c, &c->u_mode.siv.s2v_cmac, zero, GCRY_SIV_BLOCK_LEN); + if (err != 0) + return err; + + err = _gcry_cmac_final (c, &c->u_mode.siv.s2v_cmac); + if (err != 0) + return err; + + memcpy (c->u_mode.siv.s2v_zero_block, c->u_mode.siv.s2v_cmac.u_iv.iv, + GCRY_SIV_BLOCK_LEN); + memcpy (c->u_mode.siv.s2v_d, c->u_mode.siv.s2v_zero_block, + GCRY_SIV_BLOCK_LEN); + if (err != 0) + return err; + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_authenticate (gcry_cipher_hd_t c, + const byte *aadbuf, size_t aadbuflen) +{ + gcry_err_code_t err; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + if (c->marks.tag) + return GPG_ERR_INV_STATE; + if (c->marks.iv) + return GPG_ERR_INV_STATE; + + if (c->u_mode.siv.aad_count >= 126) + return GPG_ERR_INV_STATE; /* Too many AD vector components. */ + + c->u_mode.siv.aad_count++; + + _gcry_cmac_reset (&c->u_mode.siv.s2v_cmac); + + err = _gcry_cmac_write (c, &c->u_mode.siv.s2v_cmac, aadbuf, aadbuflen); + if (err != 0) + return err; + + err = _gcry_cmac_final (c, &c->u_mode.siv.s2v_cmac); + if (err != 0) + return err; + + s2v_double (c->u_mode.siv.s2v_d); + cipher_block_xor_1 (c->u_mode.siv.s2v_d, c->u_mode.siv.s2v_cmac.u_iv.iv, + GCRY_SIV_BLOCK_LEN); + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_set_nonce (gcry_cipher_hd_t c, const byte *nonce, + size_t noncelen) +{ + gcry_err_code_t err; + + err = _gcry_cipher_siv_authenticate (c, nonce, noncelen); + if (err) + return err; + + /* Nonce is the last AD before plaintext. */ + c->marks.iv = 1; + + return 0; +} + + +static gcry_err_code_t +s2v_plaintext (gcry_cipher_hd_t c, const byte *plain, size_t plainlen) +{ + gcry_err_code_t err; + + if (c->u_mode.siv.aad_count >= 127) + return GPG_ERR_INV_STATE; /* Too many AD vector components. */ + + _gcry_cmac_reset (&c->u_mode.siv.s2v_cmac); + + if (plainlen >= GCRY_SIV_BLOCK_LEN) + { + err = _gcry_cmac_write (c, &c->u_mode.siv.s2v_cmac, plain, + plainlen - GCRY_SIV_BLOCK_LEN); + if (err) + return err; + + cipher_block_xor_1 (c->u_mode.siv.s2v_d, + plain + plainlen - GCRY_SIV_BLOCK_LEN, + GCRY_SIV_BLOCK_LEN); + + err = _gcry_cmac_write (c, &c->u_mode.siv.s2v_cmac, c->u_mode.siv.s2v_d, + GCRY_SIV_BLOCK_LEN); + if (err) + return err; + } + else + { + unsigned char pad_sn[GCRY_SIV_BLOCK_LEN]; + + s2v_double (c->u_mode.siv.s2v_d); + s2v_pad (pad_sn, plain, plainlen); + cipher_block_xor_1 (pad_sn, c->u_mode.siv.s2v_d, GCRY_SIV_BLOCK_LEN); + + err = _gcry_cmac_write (c, &c->u_mode.siv.s2v_cmac, pad_sn, + GCRY_SIV_BLOCK_LEN); + wipememory (pad_sn, sizeof(pad_sn)); + if (err) + return err; + } + + c->u_mode.siv.aad_count++; + + return _gcry_cmac_final (c, &c->u_mode.siv.s2v_cmac); +} + + +gcry_err_code_t +_gcry_cipher_siv_encrypt (gcry_cipher_hd_t c, + byte *outbuf, size_t outbuflen, + const byte *inbuf, size_t inbuflen) +{ + gcry_err_code_t err; + u64 q_lo; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + if (c->marks.tag) + return GPG_ERR_INV_STATE; + if (c->u_mode.siv.dec_tag_set) + return GPG_ERR_INV_STATE; + + /* Pass plaintext to S2V. */ + err = s2v_plaintext (c, inbuf, inbuflen); + if (err != 0) + return err; + + /* Clear 31th and 63th bits. */ + memcpy (c->u_ctr.ctr, c->u_mode.siv.s2v_cmac.u_iv.iv, GCRY_SIV_BLOCK_LEN); + q_lo = buf_get_be64(c->u_ctr.ctr + 8); + q_lo &= ~((u64)1 << 31); + q_lo &= ~((u64)1 << 63); + buf_put_be64(c->u_ctr.ctr + 8, q_lo); + + /* Encrypt plaintext. */ + err = _gcry_cipher_ctr_encrypt_ctx(c, outbuf, outbuflen, inbuf, inbuflen, + c->u_mode.siv.ctr_context); + if (err != 0) + return err; + + c->marks.tag = 1; + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_set_decryption_tag (gcry_cipher_hd_t c, + const byte *tag, size_t taglen) +{ + if (taglen != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_INV_ARG; + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + if (c->marks.tag) + return GPG_ERR_INV_STATE; + + memcpy (&c->u_mode.siv.dec_tag, tag, GCRY_SIV_BLOCK_LEN); + c->u_mode.siv.dec_tag_set = 1; + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_decrypt (gcry_cipher_hd_t c, + byte *outbuf, size_t outbuflen, + const byte *inbuf, size_t inbuflen) +{ + gcry_err_code_t err; + u64 q_lo; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + if (outbuflen < inbuflen) + return GPG_ERR_BUFFER_TOO_SHORT; + if (c->marks.tag) + return GPG_ERR_INV_STATE; + if (!c->u_mode.siv.dec_tag_set) + return GPG_ERR_INV_STATE; + + /* Clear 31th and 63th bits. */ + memcpy (c->u_ctr.ctr, c->u_mode.siv.dec_tag, GCRY_SIV_BLOCK_LEN); + q_lo = buf_get_be64(c->u_ctr.ctr + 8); + q_lo &= ~((u64)1 << 31); + q_lo &= ~((u64)1 << 63); + buf_put_be64(c->u_ctr.ctr + 8, q_lo); + + /* Decrypt ciphertext. */ + err = _gcry_cipher_ctr_encrypt_ctx(c, outbuf, outbuflen, inbuf, inbuflen, + c->u_mode.siv.ctr_context); + if (err != 0) + return err; + + /* Pass plaintext to S2V. */ + err = s2v_plaintext (c, outbuf, inbuflen); + if (err != 0) + return err; + + c->marks.tag = 1; + + if (!buf_eq_const(c->u_mode.siv.s2v_cmac.u_iv.iv, c->u_mode.siv.dec_tag, + GCRY_SIV_BLOCK_LEN)) + { + wipememory (outbuf, inbuflen); + return GPG_ERR_CHECKSUM; + } + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_get_tag (gcry_cipher_hd_t c, unsigned char *outbuf, + size_t outbuflen) +{ + gcry_err_code_t err; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + if (c->u_mode.siv.dec_tag_set) + return GPG_ERR_INV_STATE; + + if (!c->marks.tag) + { + /* Finalize SIV with zero-length plaintext. */ + err = s2v_plaintext (c, NULL, 0); + if (err != 0) + return err; + + c->marks.tag = 1; + } + + if (outbuflen > GCRY_SIV_BLOCK_LEN) + outbuflen = GCRY_SIV_BLOCK_LEN; + + /* We already checked that OUTBUF is large enough to hold + * the result or has valid truncated length. */ + memcpy (outbuf, c->u_mode.siv.s2v_cmac.u_iv.iv, outbuflen); + + return 0; +} + + +gcry_err_code_t +_gcry_cipher_siv_check_tag (gcry_cipher_hd_t c, const unsigned char *intag, + size_t taglen) +{ + gcry_err_code_t err; + size_t n; + + if (c->spec->blocksize != GCRY_SIV_BLOCK_LEN) + return GPG_ERR_CIPHER_ALGO; + + if (!c->marks.tag) + { + /* Finalize SIV with zero-length plaintext. */ + err = s2v_plaintext (c, NULL, 0); + if (err != 0) + return err; + + c->marks.tag = 1; + } + + n = GCRY_SIV_BLOCK_LEN; + if (taglen < n) + n = taglen; + + if (!buf_eq_const(c->u_mode.siv.s2v_cmac.u_iv.iv, intag, n) + || GCRY_SIV_BLOCK_LEN != taglen) + { + return GPG_ERR_CHECKSUM; + } + + return 0; +} diff --git a/cipher/cipher.c b/cipher/cipher.c index 1039dff7..a274466f 100644 --- a/cipher/cipher.c +++ b/cipher/cipher.c @@ -550,6 +550,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle, case GCRY_CIPHER_MODE_CMAC: case GCRY_CIPHER_MODE_EAX: case GCRY_CIPHER_MODE_GCM: + case GCRY_CIPHER_MODE_SIV: if (!spec->encrypt || !spec->decrypt) err = GPG_ERR_INV_CIPHER_MODE; break; @@ -609,6 +610,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle, switch (mode) { case GCRY_CIPHER_MODE_XTS: + case GCRY_CIPHER_MODE_SIV: /* Additional cipher context for tweak. */ size += 2 * spec->contextsize + 15; break; @@ -661,7 +663,12 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle, tc = h->context.c + spec->contextsize * 2; tc += (16 - (uintptr_t)tc % 16) % 16; h->u_mode.xts.tweak_context = tc; + break; + case GCRY_CIPHER_MODE_SIV: + tc = h->context.c + spec->contextsize * 2; + tc += (16 - (uintptr_t)tc % 16) % 16; + h->u_mode.siv.ctr_context = tc; break; default: @@ -731,6 +738,13 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen) return GPG_ERR_WEAK_KEY; } } + else if (c->mode == GCRY_CIPHER_MODE_SIV) + { + /* SIV uses two keys. */ + if (keylen % 2) + return GPG_ERR_INV_KEYLEN; + keylen /= 2; + } rc = c->spec->setkey (&c->context.c, key, keylen, &c->bulk); if (!rc || (c->marks.allow_weak_key && rc == GPG_ERR_WEAK_KEY)) @@ -777,9 +791,22 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen) c->marks.key = 0; break; + case GCRY_CIPHER_MODE_SIV: + /* Setup CTR cipher with second part of SIV key. */ + rc = _gcry_cipher_siv_setkey (c, key + keylen, keylen); + if (!rc || (c->marks.allow_weak_key && rc == GPG_ERR_WEAK_KEY)) + { + /* Duplicate initial CTR context. */ + memcpy (c->u_mode.siv.ctr_context + c->spec->contextsize, + c->u_mode.siv.ctr_context, c->spec->contextsize); + } + else + c->marks.key = 0; + break; + default: break; - }; + } } else c->marks.key = 0; @@ -876,14 +903,25 @@ cipher_reset (gcry_cipher_hd_t c) break; case GCRY_CIPHER_MODE_OCB: - /* Do not clear precalculated L-values */ { + const size_t table_maxblks = 1 << OCB_L_TABLE_SIZE; byte *u_mode_head_pos = (void *)&c->u_mode.ocb; byte *u_mode_tail_pos = (void *)&c->u_mode.ocb.tag; size_t u_mode_head_length = u_mode_tail_pos - u_mode_head_pos; size_t u_mode_tail_length = sizeof(c->u_mode.ocb) - u_mode_head_length; - memset (u_mode_tail_pos, 0, u_mode_tail_length); + if (c->u_mode.ocb.aad_nblocks < table_maxblks) + { + /* Precalculated L-values are still ok after reset, no need + * to clear. */ + memset (u_mode_tail_pos, 0, u_mode_tail_length); + } + else + { + /* Reinitialize L table. */ + memset (&c->u_mode.ocb, 0, sizeof(c->u_mode.ocb)); + _gcry_cipher_ocb_setkey (c); + } /* Setup default taglen. */ c->u_mode.ocb.taglen = 16; @@ -896,6 +934,24 @@ cipher_reset (gcry_cipher_hd_t c) c->spec->contextsize); break; + case GCRY_CIPHER_MODE_SIV: + /* Only clear head of u_mode, keep s2v_cmac and ctr_context. */ + { + byte *u_mode_pos = (void *)&c->u_mode; + byte *tail_pos = (void *)&c->u_mode.siv.s2v_cmac; + size_t u_mode_head_length = tail_pos - u_mode_pos; + + memset (&c->u_mode, 0, u_mode_head_length); + + memcpy (c->u_mode.siv.ctr_context, + c->u_mode.siv.ctr_context + c->spec->contextsize, + c->spec->contextsize); + + memcpy (c->u_mode.siv.s2v_d, c->u_mode.siv.s2v_zero_block, + GCRY_SIV_BLOCK_LEN); + } + break; + default: break; /* u_mode unused by other modes. */ } @@ -1314,6 +1370,11 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode) c->mode_ops.decrypt = _gcry_cipher_xts_decrypt; break; + case GCRY_CIPHER_MODE_SIV: + c->mode_ops.encrypt = _gcry_cipher_siv_encrypt; + c->mode_ops.decrypt = _gcry_cipher_siv_decrypt; + break; + default: c->mode_ops.encrypt = do_encrypt_none_unknown; c->mode_ops.decrypt = do_decrypt_none_unknown; @@ -1343,6 +1404,10 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode) c->mode_ops.setiv = _gcry_cipher_ocb_set_nonce; break; + case GCRY_CIPHER_MODE_SIV: + c->mode_ops.setiv = _gcry_cipher_siv_set_nonce; + break; + default: c->mode_ops.setiv = cipher_setiv; break; @@ -1388,6 +1453,12 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode) c->mode_ops.check_tag = _gcry_cipher_ocb_check_tag; break; + case GCRY_CIPHER_MODE_SIV: + c->mode_ops.authenticate = _gcry_cipher_siv_authenticate; + c->mode_ops.get_tag = _gcry_cipher_siv_get_tag; + c->mode_ops.check_tag = _gcry_cipher_siv_check_tag; + break; + default: c->mode_ops.authenticate = NULL; c->mode_ops.get_tag = NULL; @@ -1462,6 +1533,18 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen) } break; + case GCRYCTL_SET_DECRYPTION_TAG: + { + if (!buffer) + return GPG_ERR_INV_ARG; + + if (h->mode == GCRY_CIPHER_MODE_SIV) + rc = _gcry_cipher_siv_set_decryption_tag (h, buffer, buflen); + else + rc = GPG_ERR_INV_CIPHER_MODE; + } + break; + case GCRYCTL_SET_TAGLEN: if (!h || !buffer || buflen != sizeof(int) ) return GPG_ERR_INV_ARG; @@ -1595,6 +1678,10 @@ _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes) *nbytes = POLY1305_TAGLEN; break; + case GCRY_CIPHER_MODE_SIV: + *nbytes = GCRY_SIV_BLOCK_LEN; + break; + default: rc = GPG_ERR_INV_CIPHER_MODE; break; diff --git a/doc/gcrypt.texi b/doc/gcrypt.texi index 148a5fa2..e5c4b64e 100644 --- a/doc/gcrypt.texi +++ b/doc/gcrypt.texi @@ -1760,6 +1760,28 @@ EAX is an Authenticated Encryption with Associated Data (AEAD) block cipher mode by Bellare, Rogaway, and Wagner (see @uref{http://web.cs.ucdavis.edu/~rogaway/papers/eax.html}). +@item GCRY_CIPHER_MODE_SIV +@cindex SIV, SIV mode +Synthetic Initialization Vector (SIV) is an Authenticated Encryption +with Associated Data (AEAD) block cipher mode, which is specified in +RFC-5297. This mode works with block ciphers with block size of 128 +bits and uses tag length of 128 bits. Depending on how it is used, +SIV achieves either the goal of deterministic authenticated encryption +or the goal of nonce-based, misuse-resistant authenticated encryption. + +The SIV mode requires doubling key-length, for example, using 512-bit +key with AES-256 (@code{GCRY_CIPHER_AES256}). Multiple AD instances can +be passed to SIV mode with separate calls to +@code{gcry_cipher_authenticate}. Nonce may be passed either through +@code{gcry_cipher_setiv} or in the last call to +@code{gcry_cipher_authenticate}. Note that use of @code{gcry_cipher_setiv} +blocks any further calls to @code{gcry_cipher_authenticate} as nonce needs +to be the last AD element with the SIV mode. When encrypting or decrypting, +full-sized plaintext or ciphertext needs to be passed to +@code{gcry_cipher_encrypt} or @code{gcry_cipher_decrypt}. Decryption tag +needs to be given to SIV mode before decryption using +@code{gcry_cipher_set_decryption_tag}. + @end table @node Working with cipher handles @@ -1794,8 +1816,9 @@ ChaCha20 stream cipher. The block cipher modes @code{GCRY_CIPHER_MODE_CTR} and @code{GCRY_CIPHER_MODE_EAX}) will work with any block cipher algorithm. GCM mode (@code{GCRY_CIPHER_MODE_GCM}), CCM mode (@code{GCRY_CIPHER_MODE_CCM}), -OCB mode (@code{GCRY_CIPHER_MODE_OCB}), and XTS mode -(@code{GCRY_CIPHER_MODE_XTS}) will only work with block cipher +OCB mode (@code{GCRY_CIPHER_MODE_OCB}), XTS mode +(@code{GCRY_CIPHER_MODE_XTS}) and SIV mode +(@code{GCRY_CIPHER_MODE_SIV}) will only work with block cipher algorithms which have the block size of 16 bytes. The third argument @var{flags} can either be passed as @code{0} or as @@ -1988,6 +2011,16 @@ implemented as a macro. @end deftypefun +The SIV mode requires decryption tag to be input before decryption. +This is done with: + +@deftypefun gcry_error_t gcry_cipher_set_decryption_tag (gcry_cipher_hd_t @var{h}, const void *@var{tag}, size_t @var{taglen}) + +Set decryption tag for the SIV mode decryption. This is implemented +as a macro. +@end deftypefun + + OpenPGP (as defined in RFC-4880) requires a special sync operation in some places. The following function is used for this: diff --git a/src/gcrypt.h.in b/src/gcrypt.h.in index 882f4387..99b21276 100644 --- a/src/gcrypt.h.in +++ b/src/gcrypt.h.in @@ -334,7 +334,8 @@ enum gcry_ctl_cmds GCRYCTL_GET_TAGLEN = 76, GCRYCTL_REINIT_SYSCALL_CLAMP = 77, GCRYCTL_AUTO_EXPAND_SECMEM = 78, - GCRYCTL_SET_ALLOW_WEAK_KEY = 79 + GCRYCTL_SET_ALLOW_WEAK_KEY = 79, + GCRYCTL_SET_DECRYPTION_TAG = 80 }; /* Perform various operations defined by CMD. */ @@ -975,7 +976,8 @@ enum gcry_cipher_modes GCRY_CIPHER_MODE_OCB = 11, /* OCB3 mode. */ GCRY_CIPHER_MODE_CFB8 = 12, /* Cipher feedback (8 bit mode). */ GCRY_CIPHER_MODE_XTS = 13, /* XTS mode. */ - GCRY_CIPHER_MODE_EAX = 14 /* EAX mode. */ + GCRY_CIPHER_MODE_EAX = 14, /* EAX mode. */ + GCRY_CIPHER_MODE_SIV = 15 /* SIV mode. */ }; /* Flags used with the open function. */ @@ -999,6 +1001,9 @@ enum gcry_cipher_flags /* XTS works only with blocks of 128 bits. */ #define GCRY_XTS_BLOCK_LEN (128 / 8) +/* SIV works only with blocks of 128 bits */ +#define GCRY_SIV_BLOCK_LEN (128 / 8) + /* Create a handle for algorithm ALGO to be used in MODE. FLAGS may be given as an bitwise OR of the gcry_cipher_flags values. */ gcry_error_t gcry_cipher_open (gcry_cipher_hd_t *handle, @@ -1101,6 +1106,11 @@ size_t gcry_cipher_get_algo_blklen (int algo); #define gcry_cipher_test_algo(a) \ gcry_cipher_algo_info( (a), GCRYCTL_TEST_ALGO, NULL, NULL ) +/* Setup tag for decryption (for SIV mode). */ +#define gcry_cipher_set_decryption_tag(a, tag, taglen) \ + gcry_cipher_ctl ((a), GCRYCTL_SET_DECRYPTION_TAG, \ + (void *)(tag), (taglen)) + /************************************ * * diff --git a/tests/basic.c b/tests/basic.c index 8d29c14e..989a5aca 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -4801,6 +4801,426 @@ check_eax_cipher (void) static void +check_siv_cipher (void) +{ + static const struct tv + { + int algo; + char key[MAX_DATA_LEN]; + char ad1[MAX_DATA_LEN]; + int ad1len; + char ad2[MAX_DATA_LEN]; + int ad2len; + char nonce[MAX_DATA_LEN]; + int noncelen; + unsigned char plaintext[MAX_DATA_LEN]; + int inlen; + char tag[MAX_DATA_LEN]; + char out[MAX_DATA_LEN]; + } tv[] = + { + /* Test vectors from RFC5297 */ + { + GCRY_CIPHER_AES128, + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27", + 24, + "", + -1, + "", + -1, + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee", + 14, + "\x85\x63\x2d\x07\xc6\xe8\xf3\x7f\x95\x0a\xcd\x32\x0a\x2e\xcc\x93", + "\x40\xc0\x2b\x96\x90\xc4\xdc\x04\xda\xef\x7f\x6a\xfe\x5c" + }, + { + GCRY_CIPHER_AES128, + "\x7f\x7e\x7d\x7c\x7b\x7a\x79\x78\x77\x76\x75\x74\x73\x72\x71\x70" + "\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f", + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff" + "\xde\xad\xda\xda\xde\xad\xda\xda\xff\xee\xdd\xcc\xbb\xaa\x99\x88" + "\x77\x66\x55\x44\x33\x22\x11\x00", + 40, + "\x10\x20\x30\x40\x50\x60\x70\x80\x90\xa0", + 10, + "\x09\xf9\x11\x02\x9d\x74\xe3\x5b\xd8\x41\x56\xc5\x63\x56\x88\xc0", + 16, + "\x74\x68\x69\x73\x20\x69\x73\x20\x73\x6f\x6d\x65\x20\x70\x6c\x61" + "\x69\x6e\x74\x65\x78\x74\x20\x74\x6f\x20\x65\x6e\x63\x72\x79\x70" + "\x74\x20\x75\x73\x69\x6e\x67\x20\x53\x49\x56\x2d\x41\x45\x53", + 47, + "\x7b\xdb\x6e\x3b\x43\x26\x67\xeb\x06\xf4\xd1\x4b\xff\x2f\xbd\x0f", + "\xcb\x90\x0f\x2f\xdd\xbe\x40\x43\x26\x60\x19\x65\xc8\x89\xbf\x17" + "\xdb\xa7\x7c\xeb\x09\x4f\xa6\x63\xb7\xa3\xf7\x48\xba\x8a\xf8\x29" + "\xea\x64\xad\x54\x4a\x27\x2e\x9c\x48\x5b\x62\xa3\xfd\x5c\x0d" + }, + /* From libaes_siv */ + { + GCRY_CIPHER_AES256, + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff" + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0", + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f" + "\x20\x21\x22\x23\x24\x25\x26\x27", + 24, + "", + -1, + "", + -1, + "\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee", + 14, + "\x72\x4d\xfb\x2e\xaf\x94\xdb\xb1\x9b\x0b\xa3\xa2\x99\xa0\x80\x1e", + "\xf3\xb0\x5a\x55\x49\x8e\xc2\x55\x26\x90\xb8\x98\x10\xe4" + }, + /* From https://github.com/cryptomator/siv-mode */ + { GCRY_CIPHER_AES128, + "\x90\xe5\x90\xae\xca\x19\x70\xed\xd1\x9f\xe5\x0f\xa6\x91\xae\x12" + "\x34\x2c\x49\x7a\x22\xc2\x4f\xaa\x9e\x87\x19\x2e\x34\x00\xfb\xce", + "\x2d\xdf\x87\xac\x97\x5d\x0c", + 7, + "", + -1, + "", + -1, + "\x44", + 1, + "\x7b\x0d\xdd\x88\x74\x39\x43\xc6\x44\xc1\xd1\xa2\x18\xa3\x1e\xdf", + "\x2e" + }, + { + GCRY_CIPHER_AES128, + "\xf6\xde\x98\x19\x31\x1b\xd3\xde\x0b\xd1\x98\x70\x9d\xea\x9f\xdf" + "\xb8\x2e\x80\x44\xe4\x00\x13\x2a\x90\xff\xe9\xa9\xde\x81\x44\x75", + "\x7b\xd3\x6f\x24\x09\xfc\xd0\x0f\x5c\xcd\x9a\xf2\xe3\xf5\x76\x45" + "\xf7\xc5\x3f\x39\xf7\xad\xcb\xf0\x7a\x0e\x43\x30\x7e\x55\xa2\x53" + "\x47\x49\x48\x20\x20\x27\x6c\x8a\x20\x44\x22\xcd\x26\xbf\x7e\x89" + "\x88\x38\x0d\x94\xff\x12\xc5\x18\xfd\x20\x2c\x2a\x1b\x00\xb3", + 63, + "", + -1, + "", + -1, + "", + 0, + "\x4c\x0e\xc2\xcc\x61\x59\xb1\x17\xdb\x98\x6d\x9a\xa5\xb4\xa0\x11", + "" + }, + /* From https://github.com/RustCrypto/AEADs */ + { + GCRY_CIPHER_AES128, + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + "", + -1, + "", + -1, + "", + -1, + "", + 0, + "\xf2\x00\x7a\x5b\xeb\x2b\x89\x00\xc5\x88\xa7\xad\xf5\x99\xf1\x72", + "" + }, + { + GCRY_CIPHER_AES128, + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + "", + -1, + "", + -1, + "", + -1, + "\x00\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa\xbb\xcc\xdd\xee\xff", + 16, + "\xf3\x04\xf9\x12\x86\x3e\x30\x3d\x5b\x54\x0e\x50\x57\xc7\x01\x0c", + "\x94\x2f\xfa\xf4\x5b\x0e\x5c\xa5\xfb\x9a\x56\xa5\x26\x3b\xb0\x65" + }, + /* From nettle */ + { + GCRY_CIPHER_AES128, + "\xff\xfe\xfd\xfc\xfb\xfa\xf9\xf8\xf7\xf6\xf5\xf4\xf3\xf2\xf1\xf0" + "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff", + "", + 0, + "", + -1, + "\x01", + 1, + "", + 0, + "\xc6\x96\xf8\x4f\xdf\x92\xab\xa3\xc3\x1c\x23\xd5\xf2\x08\x75\x13", + "" + }, + /* From botan */ + { + GCRY_CIPHER_AES128, + "\x2a\x83\xf6\x10\xa1\xd1\x77\xec\x2e\x00\x89\x80\xdc\x02\xa6\x6e" + "\xeb\x75\xaf\x6c\xba\x44\xa4\xe0\x9f\x3d\x93\xea\x1f\xa2\x88\x67", + "", + 0, + "", + -1, + "", + -1, + "", + 0, + "\x6b\xc5\xca\x86\x32\x29\x66\x75\x18\xa9\xab\xbd\x5a\xe6\xc1\xd5", + "" + }, + { + GCRY_CIPHER_AES128, + "\x97\xef\x57\xd4\xe2\xe9\x2f\x14\xdf\x73\x31\xfb\xa3\xd9\xf3\x58" + "\x87\xdd\xe7\xad\x86\x91\xfb\x80\x17\x68\x58\xd6\x59\x20\x14\x27", + "", + 0, + "", + -1, + "", + -1, + "\x75\x73\x97\x4d\x6f\xa7\x65\xbc\xd0\xe6\x23\x2c\x24\x0e\x82\x7e", + 16, + "\x68\x60\xa9\xc7\xbf\x4a\x6b\x21\x92\x44\xd7\xa8\xea\xa1\xf5\x0c", + "\x6f\x97\x93\x82\xcd\xe6\x8d\xe6\x0a\xb2\xad\x09\x53\x60\x64\x85" + } + }; + + 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 taglen2; + + if (verbose) + fprintf (stderr, " Starting SIV checks.\n"); + + for (i = 0; i < sizeof (tv) / sizeof (tv[0]); i++) + { + if (gcry_cipher_test_algo (tv[i].algo) && in_fips_mode) + { + if (verbose) + fprintf (stderr, " algorithm %d not available in fips mode\n", + tv[i].algo); + continue; + } + + if (verbose) + fprintf (stderr, " checking SIV 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_SIV, 0); + if (!err) + err = gcry_cipher_open (&hdd, tv[i].algo, GCRY_CIPHER_MODE_SIV, 0); + if (err) + { + fail ("aes-siv, gcry_cipher_open failed: %s\n", gpg_strerror (err)); + return; + } + + keylen = gcry_cipher_get_algo_keylen (tv[i].algo) * 2; + if (!keylen) + { + fail ("aes-siv, 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 ("aes-siv, gcry_cipher_setkey failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + if (tv[i].ad1len >= 0) + { + err = gcry_cipher_authenticate (hde, tv[i].ad1, tv[i].ad1len); + if (!err) + err = gcry_cipher_authenticate (hdd, tv[i].ad1, tv[i].ad1len); + if (err) + { + fail ("aes-siv, gcry_cipher_authenticate failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + } + + if (tv[i].ad2len >= 0) + { + err = gcry_cipher_authenticate (hde, tv[i].ad2, tv[i].ad2len); + if (!err) + err = gcry_cipher_authenticate (hdd, tv[i].ad2, tv[i].ad2len); + if (err) + { + fail ("aes-siv, gcry_cipher_authenticate failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + } + + if (tv[i].noncelen >= 0) + { + err = gcry_cipher_setiv (hde, tv[i].nonce, tv[i].noncelen); + if (!err) + err = gcry_cipher_setiv (hdd, tv[i].nonce, tv[i].noncelen); + if (err) + { + fail ("aes-siv, gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + /* Further AD not allowed after setting nonce. */ + err = gcry_cipher_authenticate (hde, tv[i].nonce, tv[i].noncelen); + if (!err) + { + fail ("aes-siv, gcry_cipher_authenticate after setiv did not fail\n"); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + } + + err = gcry_cipher_info (hde, GCRYCTL_GET_TAGLEN, NULL, &taglen2); + if (err) + { + fail ("cipher-siv, gcryctl_get_taglen failed (tv %d): %s\n", + i, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + if (taglen2 != 16) + { + fail ("cipher-siv, gcryctl_get_taglen returned bad length" + " (tv %d): got=%zu want=%d\n", + i, taglen2, 16); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + if (tv[i].inlen) + { + err = gcry_cipher_encrypt (hde, out, tv[i].inlen, + tv[i].plaintext, tv[i].inlen); + if (err) + { + fail ("aes-siv, gcry_cipher_encrypt (%d) 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)) + { + mismatch (tv[i].out, tv[i].inlen, out, tv[i].inlen); + fail ("aes-siv, encrypt mismatch entry %d\n", i); + } + + err = gcry_cipher_gettag (hde, tag, taglen2); + if (err) + { + fail ("aes-siv, 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, tag, taglen2)) + { + mismatch (tv[i].tag, taglen2, tag, taglen2); + fail ("aes-siv, tag mismatch entry %d\n", i); + } + + err = gcry_cipher_set_decryption_tag (hdd, tag, taglen2); + if (err) + { + fail ("aes-siv, gcry_cipher_set_decryption_tag (%d) failed: %s\n", + i, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + err = gcry_cipher_decrypt (hdd, out, tv[i].inlen, NULL, 0); + if (err) + { + fail ("aes-siv, gcry_cipher_decrypt (%d) 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 ("aes-siv, decrypt mismatch entry %d\n", i); + + err = gcry_cipher_checktag (hdd, tag, taglen2); + if (err) + { + fail ("aes-siv, gcry_cipher_checktag (%d) failed: %s\n", + i, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + } + else + { + err = gcry_cipher_gettag (hde, tag, taglen2); + if (err) + { + fail ("aes-siv, 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, tag, taglen2)) + { + mismatch (tv[i].tag, taglen2, tag, taglen2); + fail ("aes-siv, tag mismatch entry %d\n", i); + } + + err = gcry_cipher_checktag (hdd, tv[i].tag, taglen2); + if (err) + { + fail ("aes-siv, gcry_cipher_checktag (%d) 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 SIV checks.\n"); +} + + +static void _check_poly1305_cipher (unsigned int step) { static const struct tv @@ -10133,6 +10553,7 @@ check_cipher_modes(void) check_ocb_cipher (); check_xts_cipher (); check_eax_cipher (); + check_siv_cipher (); check_gost28147_cipher (); check_stream_cipher (); check_stream_cipher_large_block (); diff --git a/tests/bench-slope.c b/tests/bench-slope.c index d1b7f24f..91eb7cc5 100644 --- a/tests/bench-slope.c +++ b/tests/bench-slope.c @@ -966,6 +966,11 @@ bench_encrypt_init (struct bench_obj *obj) } keylen = gcry_cipher_get_algo_keylen (mode->algo); + if (mode->mode == GCRY_CIPHER_MODE_SIV) + { + keylen *= 2; + } + if (keylen) { char key[keylen]; @@ -1290,6 +1295,7 @@ bench_aead_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen, int err; char tag[16]; + gcry_cipher_reset (hd); gcry_cipher_setiv (hd, nonce, noncelen); gcry_cipher_final (hd); @@ -1320,13 +1326,18 @@ bench_aead_decrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen, int err; char tag[16] = { 0, }; + gcry_cipher_reset (hd); + gcry_cipher_set_decryption_tag (hd, tag, 16); + gcry_cipher_setiv (hd, nonce, noncelen); gcry_cipher_final (hd); err = gcry_cipher_decrypt (hd, buf, buflen, buf, buflen); + if (gpg_err_code (err) == GPG_ERR_CHECKSUM) + err = gpg_error (GPG_ERR_NO_ERROR); if (err) { - fprintf (stderr, PGM ": gcry_cipher_encrypt failed: %s\n", + fprintf (stderr, PGM ": gcry_cipher_decrypt failed: %s\n", gpg_strerror (err)); gcry_cipher_close (hd); exit (1); @@ -1354,13 +1365,18 @@ bench_aead_authenticate_do_bench (struct bench_obj *obj, void *buf, char tag[16] = { 0, }; char data = 0xff; - err = gcry_cipher_setiv (hd, nonce, noncelen); - if (err) + gcry_cipher_reset (hd); + + if (noncelen > 0) { - fprintf (stderr, PGM ": gcry_cipher_setiv failed: %s\n", - gpg_strerror (err)); - gcry_cipher_close (hd); - exit (1); + err = gcry_cipher_setiv (hd, nonce, noncelen); + if (err) + { + fprintf (stderr, PGM ": gcry_cipher_setiv failed: %s\n", + gpg_strerror (err)); + gcry_cipher_close (hd); + exit (1); + } } err = gcry_cipher_authenticate (hd, buf, buflen); @@ -1487,6 +1503,47 @@ static struct bench_ops ocb_authenticate_ops = { &bench_ocb_authenticate_do_bench }; + +static void +bench_siv_encrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + bench_aead_encrypt_do_bench (obj, buf, buflen, NULL, 0); +} + +static void +bench_siv_decrypt_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + bench_aead_decrypt_do_bench (obj, buf, buflen, NULL, 0); +} + +static void +bench_siv_authenticate_do_bench (struct bench_obj *obj, void *buf, + size_t buflen) +{ + bench_aead_authenticate_do_bench (obj, buf, buflen, NULL, 0); +} + +static struct bench_ops siv_encrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_siv_encrypt_do_bench +}; + +static struct bench_ops siv_decrypt_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_siv_decrypt_do_bench +}; + +static struct bench_ops siv_authenticate_ops = { + &bench_encrypt_init, + &bench_encrypt_free, + &bench_siv_authenticate_do_bench +}; + + static void bench_eax_encrypt_do_bench (struct bench_obj *obj, void *buf, size_t buflen) @@ -1603,6 +1660,9 @@ static struct bench_cipher_mode cipher_modes[] = { {GCRY_CIPHER_MODE_OCB, "OCB enc", &ocb_encrypt_ops}, {GCRY_CIPHER_MODE_OCB, "OCB dec", &ocb_decrypt_ops}, {GCRY_CIPHER_MODE_OCB, "OCB auth", &ocb_authenticate_ops}, + {GCRY_CIPHER_MODE_SIV, "SIV enc", &siv_encrypt_ops}, + {GCRY_CIPHER_MODE_SIV, "SIV dec", &siv_decrypt_ops}, + {GCRY_CIPHER_MODE_SIV, "SIV auth", &siv_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}, @@ -1651,6 +1711,10 @@ cipher_bench_one (int algo, struct bench_cipher_mode *pmode) if (mode.mode == GCRY_CIPHER_MODE_XTS && blklen != GCRY_XTS_BLOCK_LEN) return; + /* SIV has restrictions for block-size */ + if (mode.mode == GCRY_CIPHER_MODE_SIV && blklen != GCRY_SIV_BLOCK_LEN) + return; + /* Our OCB implementation has restrictions for block-size. */ if (mode.mode == GCRY_CIPHER_MODE_OCB && blklen != GCRY_OCB_BLOCK_LEN) return; |