[PATCH 1/4] Add SIV mode (RFC 5297)
Jussi Kivilinna
jussi.kivilinna at iki.fi
Fri Aug 13 17:01:26 CEST 2021
* cipher/Makefile.am: Add 'cipher-siv.c'.
* cipher/cipher-ctr.c (_gcry_cipher_ctr_encrypt): Rename to
_gcry_cipher_ctr_encrypt_ctx and add algo context parameter.
(_gcry_cipher_ctr_encrypt): New using _gcry_cipher_ctr_encrypt_ctx.
* cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_mode.siv'.
(_gcry_cipher_ctr_encrypt_ctx, _gcry_cipher_siv_encrypt)
(_gcry_cipher_siv_decrypt, _gcry_cipher_siv_set_nonce)
(_gcry_cipher_siv_authenticate, _gcry_cipher_siv_set_decryption_tag)
(_gcry_cipher_siv_get_tag, _gcry_cipher_siv_check_tag)
(_gcry_cipher_siv_setkey): New.
* cipher/cipher-siv.c: New.
* cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey)
(cipher_reset, _gcry_cipher_setup_mode_ops, _gcry_cipher_info): Add
GCRY_CIPHER_MODE_SIV handling.
(_gcry_cipher_ctl): Add GCRYCTL_SET_DECRYPTION_TAG handling.
* doc/gcrypt.texi: Add documentation for SIV mode.
* src/gcrypt.h.in (GCRYCTL_SET_DECRYPTION_TAG): New.
(GCRY_CIPHER_MODE_SIV): New.
(gcry_cipher_set_decryption_tag): New.
* tests/basic.c (check_siv_cipher): New.
(check_cipher_modes): Add call for 'check_siv_cipher'.
* tests/bench-slope.c (bench_encrypt_init): Use double size key for
SIV mode.
(bench_aead_encrypt_do_bench, bench_aead_decrypt_do_bench)
(bench_aead_authenticate_do_bench): Reset cipher context on each run.
(bench_aead_authenticate_do_bench): Support nonce-less operation.
(bench_siv_encrypt_do_bench, bench_siv_decrypt_do_bench)
(bench_siv_authenticate_do_bench, siv_encrypt_ops)
(siv_decrypt_ops, siv_authenticate_ops): New.
(cipher_modes): Add SIV mode benchmarks.
(cipher_bench_one): Restrict SIV mode testing to 16 byte block-size.
--
Signed-off-by: Jussi Kivilinna <jussi.kivilinna at iki.fi>
---
cipher/Makefile.am | 1 +
cipher/cipher-ctr.c | 21 +-
cipher/cipher-internal.h | 57 ++++++
cipher/cipher-ocb.c | 3 +-
cipher/cipher-siv.c | 377 +++++++++++++++++++++++++++++++++++
cipher/cipher.c | 93 ++++++++-
doc/gcrypt.texi | 37 +++-
src/gcrypt.h.in | 14 +-
tests/basic.c | 421 +++++++++++++++++++++++++++++++++++++++
tests/bench-slope.c | 78 +++++++-
10 files changed, 1082 insertions(+), 20 deletions(-)
create mode 100644 cipher/cipher-siv.c
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 at iki.fi>
+ *
+ * This file is part of Libgcrypt.
+ *
+ * Libgcrypt is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser general Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * Libgcrypt is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+
+#include "g10lib.h"
+#include "cipher.h"
+#include "bufhelp.h"
+#include "./cipher-internal.h"
+
+
+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}).
+ at item GCRY_CIPHER_MODE_SIV
+ at 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
+ at code{gcry_cipher_authenticate}. Nonce may be passed either through
+ at code{gcry_cipher_setiv} or in the last call to
+ at 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
+ at code{gcry_cipher_encrypt} or @code{gcry_cipher_decrypt}. Decryption tag
+needs to be given to SIV mode before decryption using
+ at 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:
+
+ at 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.
+ at 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
@@ -4800,6 +4800,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)
{
@@ -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;
--
2.30.2
More information about the Gcrypt-devel
mailing list