Blame SOURCES/gnutls-3.6.8-aead-cipher-encryptv2.patch

cde47b
From 38c8dc4317296624cba5b2c8ddba6e9047048180 Mon Sep 17 00:00:00 2001
cde47b
From: Daiki Ueno <dueno@redhat.com>
cde47b
Date: Thu, 1 Aug 2019 17:41:45 +0200
cde47b
Subject: [PATCH 1/3] iov: add iterator interface for giovec_t
cde47b
cde47b
This adds an iterator interface over giovec_t array, extracting a
cde47b
fixed sized block.
cde47b
cde47b
Signed-off-by: Daiki Ueno <dueno@redhat.com>
cde47b
---
cde47b
 .gitignore        |   1 +
cde47b
 lib/Makefile.am   |   3 +-
cde47b
 lib/iov.c         | 120 ++++++++++++++++++++++++++++++++
cde47b
 lib/iov.h         |  46 +++++++++++++
cde47b
 lib/libgnutls.map |   3 +
cde47b
 tests/Makefile.am |   6 +-
cde47b
 tests/iov.c       | 170 ++++++++++++++++++++++++++++++++++++++++++++++
cde47b
 7 files changed, 347 insertions(+), 2 deletions(-)
cde47b
 create mode 100644 lib/iov.c
cde47b
 create mode 100644 lib/iov.h
cde47b
 create mode 100644 tests/iov.c
cde47b
cde47b
diff --git a/lib/Makefile.am b/lib/Makefile.am
cde47b
index ffc72e4c2..9fe78afbd 100644
cde47b
--- a/lib/Makefile.am
cde47b
+++ b/lib/Makefile.am
cde47b
@@ -80,7 +80,8 @@ COBJECTS = range.c record.c compress.c debug.c cipher.c gthreads.h handshake-tls
cde47b
 	system-keys.h urls.c urls.h prf.c auto-verify.c dh-session.c \
cde47b
 	cert-session.c handshake-checks.c dtls-sw.c dh-primes.c openpgp_compat.c \
cde47b
 	crypto-selftests.c crypto-selftests-pk.c secrets.c extv.c extv.h \
cde47b
-	hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c
cde47b
+	hello_ext_lib.c hello_ext_lib.h ocsp-api.c stek.c cert-cred-rawpk.c \
cde47b
+	iov.c iov.h
cde47b
 
cde47b
 if WINDOWS
cde47b
 COBJECTS += system/keys-win.c
cde47b
diff --git a/lib/iov.c b/lib/iov.c
cde47b
new file mode 100644
cde47b
index 000000000..5dc29c54b
cde47b
--- /dev/null
cde47b
+++ b/lib/iov.c
cde47b
@@ -0,0 +1,120 @@
cde47b
+/*
cde47b
+ * Copyright (C) 2019 Red Hat, Inc.
cde47b
+ *
cde47b
+ * Author: Daiki Ueno
cde47b
+ *
cde47b
+ * This file is part of GnuTLS.
cde47b
+ *
cde47b
+ * The GnuTLS is free software; you can redistribute it and/or
cde47b
+ * modify it under the terms of the GNU Lesser General Public License
cde47b
+ * as published by the Free Software Foundation; either version 2.1 of
cde47b
+ * the License, or (at your option) any later version.
cde47b
+ *
cde47b
+ * This library is distributed in the hope that it will be useful, but
cde47b
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
cde47b
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
cde47b
+ * Lesser General Public License for more details.
cde47b
+ *
cde47b
+ * You should have received a copy of the GNU Lesser General Public License
cde47b
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
cde47b
+ *
cde47b
+ */
cde47b
+
cde47b
+#include "gnutls_int.h"
cde47b
+#include "iov.h"
cde47b
+
cde47b
+/**
cde47b
+ * _gnutls_iov_iter_init:
cde47b
+ * @iter: the iterator
cde47b
+ * @iov: the data buffers
cde47b
+ * @iov_count: the number of data buffers
cde47b
+ * @block_size: block size to iterate
cde47b
+ *
cde47b
+ * Initialize the iterator.
cde47b
+ *
cde47b
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, otherwise
cde47b
+ *   an error code is returned
cde47b
+ */
cde47b
+int
cde47b
+_gnutls_iov_iter_init(struct iov_iter_st *iter,
cde47b
+		      const giovec_t *iov, size_t iov_count,
cde47b
+		      size_t block_size)
cde47b
+{
cde47b
+	if (unlikely(block_size > MAX_CIPHER_BLOCK_SIZE))
cde47b
+		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
cde47b
+
cde47b
+	iter->iov = iov;
cde47b
+	iter->iov_count = iov_count;
cde47b
+	iter->iov_index = 0;
cde47b
+	iter->iov_offset = 0;
cde47b
+	iter->block_size = block_size;
cde47b
+	iter->block_offset = 0;
cde47b
+	return 0;
cde47b
+}
cde47b
+
cde47b
+/**
cde47b
+ * _gnutls_iov_iter_next:
cde47b
+ * @iter: the iterator
cde47b
+ * @data: the return location of extracted data
cde47b
+ *
cde47b
+ * Retrieve block(s) pointed by @iter and advance it to the next
cde47b
+ * position.  It returns the number of consecutive blocks in @data.
cde47b
+ * At the end of iteration, 0 is returned.
cde47b
+ *
cde47b
+ * If the data stored in @iter is not multiple of the block size, the
cde47b
+ * remaining data is stored in the "block" field of @iter with the
cde47b
+ * size stored in the "block_offset" field.
cde47b
+ *
cde47b
+ * Returns: On success, a value greater than or equal to zero is
cde47b
+ *   returned, otherwise a negative error code is returned
cde47b
+ */
cde47b
+ssize_t
cde47b
+_gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data)
cde47b
+{
cde47b
+	while (iter->iov_index < iter->iov_count) {
cde47b
+		const giovec_t *iov = &iter->iov[iter->iov_index];
cde47b
+		uint8_t *p = iov->iov_base;
cde47b
+		size_t len = iov->iov_len;
cde47b
+		size_t block_left;
cde47b
+
cde47b
+		if (unlikely(len < iter->iov_offset))
cde47b
+			return gnutls_assert_val(GNUTLS_E_UNEXPECTED_PACKET_LENGTH);
cde47b
+		len -= iter->iov_offset;
cde47b
+		p += iter->iov_offset;
cde47b
+
cde47b
+		/* We have at least one full block, return a whole set
cde47b
+		 * of full blocks immediately. */
cde47b
+		if (iter->block_offset == 0 && len >= iter->block_size) {
cde47b
+			if ((len % iter->block_size) == 0) {
cde47b
+				iter->iov_index++;
cde47b
+				iter->iov_offset = 0;
cde47b
+			} else
cde47b
+				iter->iov_offset +=
cde47b
+					len - (len % iter->block_size);
cde47b
+
cde47b
+			/* Return the blocks. */
cde47b
+			*data = p;
cde47b
+			return len / iter->block_size;
cde47b
+		}
cde47b
+
cde47b
+		/* We can complete one full block to return. */
cde47b
+		block_left = iter->block_size - iter->block_offset;
cde47b
+		if (len >= block_left) {
cde47b
+			memcpy(iter->block + iter->block_offset, p, block_left);
cde47b
+			iter->iov_offset += block_left;
cde47b
+			iter->block_offset = 0;
cde47b
+
cde47b
+			/* Return the filled block. */
cde47b
+			*data = iter->block;
cde47b
+			return 1;
cde47b
+		}
cde47b
+
cde47b
+		/* Not enough data for a full block, store in temp
cde47b
+		 * memory and continue. */
cde47b
+		memcpy(iter->block + iter->block_offset, p, len);
cde47b
+		iter->block_offset += len;
cde47b
+		iter->iov_index++;
cde47b
+		iter->iov_offset = 0;
cde47b
+	}
cde47b
+	return 0;
cde47b
+}
cde47b
diff --git a/lib/iov.h b/lib/iov.h
cde47b
new file mode 100644
cde47b
index 000000000..47fba559a
cde47b
--- /dev/null
cde47b
+++ b/lib/iov.h
cde47b
@@ -0,0 +1,46 @@
cde47b
+/*
cde47b
+ * Copyright (C) 2019 Red Hat, Inc.
cde47b
+ *
cde47b
+ * Author: Daiki Ueno
cde47b
+ *
cde47b
+ * This file is part of GnuTLS.
cde47b
+ *
cde47b
+ * The GnuTLS is free software; you can redistribute it and/or
cde47b
+ * modify it under the terms of the GNU Lesser General Public License
cde47b
+ * as published by the Free Software Foundation; either version 2.1 of
cde47b
+ * the License, or (at your option) any later version.
cde47b
+ *
cde47b
+ * This library is distributed in the hope that it will be useful, but
cde47b
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
cde47b
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
cde47b
+ * Lesser General Public License for more details.
cde47b
+ *
cde47b
+ * You should have received a copy of the GNU Lesser General Public License
cde47b
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
cde47b
+ *
cde47b
+ */
cde47b
+
cde47b
+#ifndef GNUTLS_LIB_IOV_H
cde47b
+#define GNUTLS_LIB_IOV_H
cde47b
+
cde47b
+#include "gnutls_int.h"
cde47b
+
cde47b
+struct iov_iter_st {
cde47b
+	const giovec_t *iov;
cde47b
+	size_t iov_count;	/* the number of iov */
cde47b
+	size_t iov_index;	/* index of the current buffer */
cde47b
+	size_t iov_offset;	/* byte offset in the current buffer */
cde47b
+
cde47b
+	uint8_t block[MAX_CIPHER_BLOCK_SIZE]; /* incomplete block for reading */
cde47b
+	size_t block_size;	/* actual block size of the cipher */
cde47b
+	size_t block_offset;	/* offset in block */
cde47b
+
cde47b
+};
cde47b
+
cde47b
+int _gnutls_iov_iter_init(struct iov_iter_st *iter,
cde47b
+			  const giovec_t *iov, size_t iov_count,
cde47b
+			  size_t block_size);
cde47b
+
cde47b
+ssize_t _gnutls_iov_iter_next(struct iov_iter_st *iter, uint8_t **data);
cde47b
+
cde47b
+#endif /* GNUTLS_LIB_IOV_H */
cde47b
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
cde47b
index 0f31f4aef..fc93c0857 100644
cde47b
--- a/lib/libgnutls.map
cde47b
+++ b/lib/libgnutls.map
cde47b
@@ -1374,4 +1374,7 @@ GNUTLS_PRIVATE_3_4 {
cde47b
 	_gnutls_global_set_gettime_function;
cde47b
 	# Internal symbols needed by tests/tls13/anti_replay.c
cde47b
 	_gnutls_anti_replay_check;
cde47b
+	# needed by tests/iov:
cde47b
+	_gnutls_iov_iter_init;
cde47b
+	_gnutls_iov_iter_next;
cde47b
 } GNUTLS_3_4;
cde47b
diff --git a/tests/Makefile.am b/tests/Makefile.am
cde47b
index a8c2d152e..a2883570f 100644
cde47b
--- a/tests/Makefile.am
cde47b
+++ b/tests/Makefile.am
cde47b
@@ -212,7 +212,7 @@ ctests += mini-record-2 simple gnutls_hm
cde47b
 	 null_retrieve_function tls-record-size-limit tls-crt_type-neg \
cde47b
 	 resume-with-stek-expiration resume-with-previous-stek rawpk-api \
cde47b
 	 tls-record-size-limit-asym dh-compute ecdh-compute \
cde47b
-	 sign-verify-deterministic
cde47b
+	 sign-verify-deterministic iov
cde47b
 
cde47b
 if HAVE_SECCOMP_TESTS
cde47b
 ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
cde47b
@@ -460,6 +460,10 @@ tls13_anti_replay_CPPFLAGS = $(AM_CPPFLAGS) \
cde47b
 	-I$(top_builddir)/gl	\
cde47b
 	$(NETTLE_CFLAGS)
cde47b
 
cde47b
+iov_CPPFLAGS = $(AM_CPPFLAGS) \
cde47b
+	-I$(top_srcdir)/gl	\
cde47b
+	-I$(top_builddir)/gl
cde47b
+
cde47b
 if ENABLE_PKCS11
cde47b
 if !WINDOWS
cde47b
 ctests += tls13/post-handshake-with-cert-pkcs11 pkcs11/tls-neg-pkcs11-no-key
cde47b
diff --git a/tests/iov.c b/tests/iov.c
cde47b
new file mode 100644
cde47b
index 000000000..eda5583a7
cde47b
--- /dev/null
cde47b
+++ b/tests/iov.c
cde47b
@@ -0,0 +1,170 @@
cde47b
+/*
cde47b
+ * Copyright (C) 2019 Red Hat, Inc.
cde47b
+ *
cde47b
+ * Author: Daiki Ueno
cde47b
+ *
cde47b
+ * This file is part of GnuTLS.
cde47b
+ *
cde47b
+ * GnuTLS is free software; you can redistribute it and/or modify it
cde47b
+ * under the terms of the GNU General Public License as published by
cde47b
+ * the Free Software Foundation; either version 3 of the License, or
cde47b
+ * (at your option) any later version.
cde47b
+ *
cde47b
+ * GnuTLS is distributed in the hope that it will be useful, but
cde47b
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
cde47b
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
cde47b
+ * General Public License for more details.
cde47b
+ *
cde47b
+ * You should have received a copy of the GNU Lesser General Public License
cde47b
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
cde47b
+ */
cde47b
+
cde47b
+#ifdef HAVE_CONFIG_H
cde47b
+#include <config.h>
cde47b
+#endif
cde47b
+
cde47b
+#include "gnutls_int.h"
cde47b
+#include "../lib/iov.h"
cde47b
+
cde47b
+#include "utils.h"
cde47b
+
cde47b
+struct exp_st {
cde47b
+	ssize_t ret;
cde47b
+	size_t iov_index;
cde47b
+	size_t iov_offset;
cde47b
+	size_t block_offset;
cde47b
+};
cde47b
+
cde47b
+struct test_st {
cde47b
+	const char *name;
cde47b
+	const giovec_t *iov;
cde47b
+	size_t iovcnt;
cde47b
+	size_t block_size;
cde47b
+	const struct exp_st *exp;
cde47b
+	size_t expcnt;
cde47b
+	size_t remaining;
cde47b
+};
cde47b
+
cde47b
+static const giovec_t iov16[] = {
cde47b
+	{(void *) "0123456789abcdef", 16},
cde47b
+	{(void *) "0123456789abcdef", 16},
cde47b
+	{(void *) "0123456789abcdef", 16},
cde47b
+	{(void *) "0123456789abcdef", 16}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp16_64[] = {
cde47b
+	{1, 3, 16, 0},
cde47b
+	{0, 0, 0, 0}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp16_32[] = {
cde47b
+	{1, 1, 16, 0},
cde47b
+	{1, 3, 16, 0},
cde47b
+	{0, 0, 0, 0}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp16_16[] = {
cde47b
+	{1, 1, 0, 0},
cde47b
+	{1, 2, 0, 0},
cde47b
+	{1, 3, 0, 0},
cde47b
+	{1, 4, 0, 0},
cde47b
+	{0, 0, 0, 0}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp16_4[] = {
cde47b
+	{4, 1, 0, 0},
cde47b
+	{4, 2, 0, 0},
cde47b
+	{4, 3, 0, 0},
cde47b
+	{4, 4, 0, 0},
cde47b
+	{0, 0, 0, 0}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp16_3[] = {
cde47b
+	{5, 0, 15, 0},
cde47b
+	{1, 1, 2, 0},
cde47b
+	{4, 1, 14, 0},
cde47b
+	{1, 2, 1, 0},
cde47b
+	{5, 3, 0, 0},
cde47b
+	{5, 3, 15, 0},
cde47b
+	{0, 0, 0, 1}
cde47b
+};
cde47b
+
cde47b
+static const giovec_t iov8[] = {
cde47b
+	{(void *) "01234567", 8},
cde47b
+	{(void *) "01234567", 8},
cde47b
+	{(void *) "01234567", 8},
cde47b
+	{(void *) "01234567", 8}
cde47b
+};
cde47b
+
cde47b
+static const struct exp_st exp8_64[] = {
cde47b
+	{0, 0, 0, 32}
cde47b
+};
cde47b
+
cde47b
+static const struct test_st tests[] = {
cde47b
+	{ "16/64", iov16, sizeof(iov16)/sizeof(iov16[0]), 64,
cde47b
+	  exp16_64, sizeof(exp16_64)/sizeof(exp16_64[0]), 0 },
cde47b
+	{ "16/32", iov16, sizeof(iov16)/sizeof(iov16[0]), 32,
cde47b
+	  exp16_32, sizeof(exp16_32)/sizeof(exp16_32[0]), 0 },
cde47b
+	{ "16/16", iov16, sizeof(iov16)/sizeof(iov16[0]), 16,
cde47b
+	  exp16_16, sizeof(exp16_16)/sizeof(exp16_16[0]), 0 },
cde47b
+	{ "16/4", iov16, sizeof(iov16)/sizeof(iov16[0]), 4,
cde47b
+	  exp16_4, sizeof(exp16_4)/sizeof(exp16_4[0]), 0 },
cde47b
+	{ "16/3", iov16, sizeof(iov16)/sizeof(iov16[0]), 3,
cde47b
+	  exp16_3, sizeof(exp16_3)/sizeof(exp16_3[0]), 1 },
cde47b
+	{ "8/64", iov8, sizeof(iov8)/sizeof(iov8[0]), 64,
cde47b
+	  exp8_64, sizeof(exp8_64)/sizeof(exp8_64[0]), 32 }
cde47b
+};
cde47b
+
cde47b
+void
cde47b
+doit (void)
cde47b
+{
cde47b
+	size_t i;
cde47b
+
cde47b
+	for (i = 0; i < sizeof(tests)/sizeof(tests[0]); i++) {
cde47b
+		struct iov_iter_st iter;
cde47b
+		const struct exp_st *exp = tests[i].exp;
cde47b
+		uint8_t *data;
cde47b
+		size_t j;
cde47b
+
cde47b
+		success("%s\n", tests[i].name);
cde47b
+		assert(_gnutls_iov_iter_init(&iter,
cde47b
+					     tests[i].iov, tests[i].iovcnt,
cde47b
+					     tests[i].block_size) == 0);
cde47b
+		for (j = 0; j < tests[i].expcnt; j++) {
cde47b
+			ssize_t ret;
cde47b
+
cde47b
+			ret = _gnutls_iov_iter_next(&iter, &data);
cde47b
+			if (ret != exp[j].ret)
cde47b
+				fail("iov_iter_next: %d != %d\n",
cde47b
+				     (int) ret, (int) exp[j].ret);
cde47b
+			else if (debug)
cde47b
+				success("iov_iter_next: %d == %d\n",
cde47b
+					(int) ret, (int) exp[j].ret);
cde47b
+			if (ret == 0)
cde47b
+				break;
cde47b
+			if (ret > 0) {
cde47b
+				if (iter.iov_index != exp[j].iov_index)
cde47b
+					fail("iter.iov_index: %u != %u\n",
cde47b
+					     (unsigned) iter.iov_index, (unsigned) exp[j].iov_index);
cde47b
+				else if (debug)
cde47b
+					success("iter.iov_index: %u == %u\n",
cde47b
+					     (unsigned) iter.iov_index, (unsigned) exp[j].iov_index);
cde47b
+				if (iter.iov_offset != exp[j].iov_offset)
cde47b
+					fail("iter.iov_offset: %u != %u\n",
cde47b
+					     (unsigned) iter.iov_offset, (unsigned) exp[j].iov_offset);
cde47b
+				else if (debug)
cde47b
+					success("iter.iov_offset: %u == %u\n",
cde47b
+					     (unsigned) iter.iov_offset, (unsigned) exp[j].iov_offset);
cde47b
+				if (iter.block_offset != exp[j].block_offset)
cde47b
+					fail("iter.block_offset: %u != %u\n",
cde47b
+					     (unsigned) iter.block_offset, (unsigned) exp[j].block_offset);
cde47b
+				else if (debug)
cde47b
+					success("iter.block_offset: %u == %u\n",
cde47b
+					     (unsigned) iter.block_offset, (unsigned) exp[j].block_offset);
cde47b
+			}
cde47b
+		}
cde47b
+		if (iter.block_offset != tests[i].remaining)
cde47b
+			fail("remaining: %u != %u\n",
cde47b
+			     (unsigned) iter.block_offset, (unsigned) tests[i].remaining);
cde47b
+	}
cde47b
+}
cde47b
-- 
cde47b
2.21.0
cde47b
cde47b
cde47b
From 9ca7a2b42168d356126e306e25211d43ea3c2e7d Mon Sep 17 00:00:00 2001
cde47b
From: Daiki Ueno <dueno@redhat.com>
cde47b
Date: Thu, 1 Aug 2019 18:13:38 +0200
cde47b
Subject: [PATCH 2/3] crypto-api: use giovec_t iterator interface for
cde47b
 aead_encryptv
cde47b
cde47b
This replaces the macros AUTH_UPDATE and ENCRYPT used in
cde47b
gnutls_aead_cipher_encryptv() with the iov_iter interface.
cde47b
cde47b
Signed-off-by: Daiki Ueno <dueno@redhat.com>
cde47b
---
cde47b
 lib/crypto-api.c | 167 ++++++++++++++++-------------------------------
cde47b
 1 file changed, 57 insertions(+), 110 deletions(-)
cde47b
cde47b
diff --git a/lib/crypto-api.c b/lib/crypto-api.c
cde47b
index 8af3f3b7d..70107fed0 100644
cde47b
--- a/lib/crypto-api.c
cde47b
+++ b/lib/crypto-api.c
cde47b
@@ -31,6 +31,7 @@
cde47b
 #include <crypto.h>
cde47b
 #include <fips.h>
cde47b
 #include "crypto-api.h"
cde47b
+#include "iov.h"
cde47b
 
cde47b
 typedef struct api_cipher_hd_st {
cde47b
 	cipher_hd_st ctx_enc;
cde47b
@@ -916,98 +917,6 @@ static int copy_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
cde47b
 	}
cde47b
 }
cde47b
 
cde47b
-#define AUTH_UPDATE_FINAL(ctx) do { \
cde47b
-	if (index) { \
cde47b
-		ret = _gnutls_cipher_auth(ctx, cache, index); \
cde47b
-		if (unlikely(ret < 0)) \
cde47b
-			return gnutls_assert_val(ret); \
cde47b
-	} \
cde47b
-	} while(0)
cde47b
-
cde47b
-#define AUTH_UPDATE(ctx, data, length) do { \
cde47b
-	if (index) { \
cde47b
-		ssize_t left = blocksize - index; \
cde47b
-		if (length < left) { \
cde47b
-			memcpy(cache+index, data, \
cde47b
-			       length); \
cde47b
-			index += length; \
cde47b
-			goto __update_done; \
cde47b
-		} else { \
cde47b
-			memcpy(cache+index, data, left); \
cde47b
-			ret = _gnutls_cipher_auth(ctx, cache, blocksize); \
cde47b
-			if (unlikely(ret < 0)) \
cde47b
-				return gnutls_assert_val(ret); \
cde47b
-			data += left; \
cde47b
-			length -= left; \
cde47b
-		} \
cde47b
-	} \
cde47b
-	if (length >= blocksize) { \
cde47b
-		ssize_t to_proc = (length/blocksize)*blocksize; \
cde47b
-		ret = _gnutls_cipher_auth(ctx, data, to_proc); \
cde47b
-		if (unlikely(ret < 0)) \
cde47b
-			return gnutls_assert_val(ret); \
cde47b
-		data += to_proc; \
cde47b
-		length -= to_proc; \
cde47b
-	} \
cde47b
-	if (length) \
cde47b
-		memcpy(cache, data, length); \
cde47b
-	index = length; \
cde47b
- __update_done: \
cde47b
-	; \
cde47b
-	} while(0)
cde47b
-
cde47b
-#define ENCRYPT_FINAL(ctx, dst, dst_size) do { \
cde47b
-	if (index) { \
cde47b
-		if (unlikely(dst_size < (ssize_t)index)) \
cde47b
-			return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); \
cde47b
-		ret = _gnutls_cipher_encrypt2(ctx, cache, index, dst, dst_size); \
cde47b
-		if (unlikely(ret < 0)) \
cde47b
-			return gnutls_assert_val(ret); \
cde47b
-		dst += index; \
cde47b
-		dst_size -= index; \
cde47b
-	} \
cde47b
-	} while(0)
cde47b
-
cde47b
-#define ENCRYPT(ctx, data, length, dst, dst_size) do { \
cde47b
-	if (index) { \
cde47b
-		ssize_t left = blocksize - index; \
cde47b
-		if (length < left) { \
cde47b
-			memcpy(cache+index, data, \
cde47b
-			       length); \
cde47b
-			index += length; \
cde47b
-			goto __encrypt_done; \
cde47b
-		} else { \
cde47b
-			if (unlikely(dst_size < blocksize)) \
cde47b
-				return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); \
cde47b
-			memcpy(cache+index, data, left); \
cde47b
-			ret = _gnutls_cipher_encrypt2(ctx, cache, blocksize, dst, dst_size); \
cde47b
-			if (unlikely(ret < 0)) \
cde47b
-				return gnutls_assert_val(ret); \
cde47b
-			data += left; \
cde47b
-			length -= left; \
cde47b
-			dst += blocksize; \
cde47b
-			dst_size -= blocksize; \
cde47b
-		} \
cde47b
-	} \
cde47b
-	if (length >= blocksize) { \
cde47b
-		ssize_t to_proc = (length/blocksize)*blocksize; \
cde47b
-		if (unlikely(dst_size < to_proc)) \
cde47b
-			return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); \
cde47b
-		ret = _gnutls_cipher_encrypt2(ctx, data, to_proc, dst, dst_size); \
cde47b
-		if (unlikely(ret < 0)) \
cde47b
-			return gnutls_assert_val(ret); \
cde47b
-		data += to_proc; \
cde47b
-		length -= to_proc; \
cde47b
-		dst += to_proc; \
cde47b
-		dst_size -= to_proc; \
cde47b
-	} \
cde47b
-	if (length) \
cde47b
-		memcpy(cache, data, length); \
cde47b
-	index = length; \
cde47b
- __encrypt_done: \
cde47b
-	; \
cde47b
-	} while(0)
cde47b
-
cde47b
 
cde47b
 /**
cde47b
  * gnutls_aead_cipher_encryptv:
cde47b
@@ -1039,14 +948,13 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
cde47b
 			    void *ctext, size_t *ctext_len)
cde47b
 {
cde47b
 	api_aead_cipher_hd_st *h = handle;
cde47b
-	int ret;
cde47b
+	ssize_t ret;
cde47b
 	uint8_t *dst;
cde47b
-	ssize_t dst_size, total = 0, len;
cde47b
+	ssize_t dst_size, total = 0;
cde47b
 	uint8_t *p;
cde47b
-	unsigned i;
cde47b
-	uint8_t cache[MAX_CIPHER_BLOCK_SIZE];
cde47b
-	unsigned index;
cde47b
 	ssize_t blocksize = handle->ctx_enc.e->blocksize;
cde47b
+	struct iov_iter_st iter;
cde47b
+	size_t blocks;
cde47b
 
cde47b
 	/* Limitation: this function provides an optimization under the internally registered
cde47b
 	 * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(),
cde47b
@@ -1088,25 +996,64 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
cde47b
 	if (unlikely(ret < 0))
cde47b
 		return gnutls_assert_val(ret);
cde47b
 
cde47b
-	index = 0;
cde47b
-	for (i = 0; i < (unsigned)auth_iovcnt; i++) {
cde47b
-		p = auth_iov[i].iov_base;
cde47b
-		len = auth_iov[i].iov_len;
cde47b
-		AUTH_UPDATE(&handle->ctx_enc, p, len);
cde47b
+	ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc, p,
cde47b
+					  blocksize * blocks);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc,
cde47b
+					  iter.block, iter.block_offset);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
 	}
cde47b
-	AUTH_UPDATE_FINAL(&handle->ctx_enc);
cde47b
 
cde47b
 	dst = ctext;
cde47b
 	dst_size = *ctext_len;
cde47b
 
cde47b
-	index = 0;
cde47b
-	for (i = 0; i < (unsigned)iovcnt; i++) {
cde47b
-		p = iov[i].iov_base;
cde47b
-		len = iov[i].iov_len;
cde47b
-		ENCRYPT(&handle->ctx_enc, p, len, dst, dst_size);
cde47b
-		total += iov[i].iov_len;
cde47b
+	ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		if (unlikely((size_t) dst_size < blocksize * blocks))
cde47b
+			return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
cde47b
+		ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p,
cde47b
+					      blocksize * blocks,
cde47b
+					      dst, dst_size);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		DECR_LEN(dst_size, blocksize * blocks);
cde47b
+		dst += blocksize * blocks;
cde47b
+		total += blocksize * blocks;
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		if (unlikely((size_t) dst_size < iter.block_offset))
cde47b
+			return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
cde47b
+		ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
cde47b
+					      iter.block, iter.block_offset,
cde47b
+					      dst, dst_size);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		DECR_LEN(dst_size, iter.block_offset);
cde47b
+		dst += iter.block_offset;
cde47b
+		total += iter.block_offset;
cde47b
 	}
cde47b
-	ENCRYPT_FINAL(&handle->ctx_enc, dst, dst_size);
cde47b
 
cde47b
 	if ((size_t)dst_size < tag_size)
cde47b
 		return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
cde47b
-- 
cde47b
2.21.0
cde47b
cde47b
cde47b
From d230011cdbbe55f429b43d818c75c8f6687cbc78 Mon Sep 17 00:00:00 2001
cde47b
From: Daiki Ueno <dueno@redhat.com>
cde47b
Date: Fri, 2 Aug 2019 07:40:44 +0200
cde47b
Subject: [PATCH 3/3] crypto-api: add gnutls_aead_cipher_{en,de}cryptv2
cde47b
cde47b
This adds an in-place equivalent of gnutls_aead_cipher_encrypt() and
cde47b
gnutls_aead_cipher_decrypt(), that works on data buffers.
cde47b
cde47b
Signed-off-by: Daiki Ueno <dueno@redhat.com>
cde47b
---
cde47b
 .gitignore                        |   1 +
cde47b
 NEWS                              |   7 +
cde47b
 devel/libgnutls-latest-x86_64.abi |  26 +++
cde47b
 devel/symbols.last                |   3 +
cde47b
 doc/Makefile.am                   |   4 +
cde47b
 doc/manpages/Makefile.am          |   2 +
cde47b
 lib/crypto-api.c                  | 356 +++++++++++++++++++++++++++++-
cde47b
 lib/includes/gnutls/crypto.h      |  14 ++
cde47b
 lib/libgnutls.map                 |   7 +
cde47b
 tests/Makefile.am                 |   2 +-
cde47b
 tests/aead-cipher-vec.c           | 123 +++++++++++
cde47b
 11 files changed, 541 insertions(+), 4 deletions(-)
cde47b
 create mode 100644 tests/aead-cipher-vec.c
cde47b
cde47b
diff --git a/doc/Makefile.am b/doc/Makefile.am
cde47b
index 6d21d7482..add63c23d 100644
cde47b
--- a/doc/Makefile.am
cde47b
+++ b/doc/Makefile.am
cde47b
@@ -635,12 +635,16 @@ FUNCS += functions/dane_verify_session_crt
cde47b
 FUNCS += functions/dane_verify_session_crt.short
cde47b
 FUNCS += functions/gnutls_aead_cipher_decrypt
cde47b
 FUNCS += functions/gnutls_aead_cipher_decrypt.short
cde47b
+FUNCS += functions/gnutls_aead_cipher_decryptv2
cde47b
+FUNCS += functions/gnutls_aead_cipher_decryptv2.short
cde47b
 FUNCS += functions/gnutls_aead_cipher_deinit
cde47b
 FUNCS += functions/gnutls_aead_cipher_deinit.short
cde47b
 FUNCS += functions/gnutls_aead_cipher_encrypt
cde47b
 FUNCS += functions/gnutls_aead_cipher_encrypt.short
cde47b
 FUNCS += functions/gnutls_aead_cipher_encryptv
cde47b
 FUNCS += functions/gnutls_aead_cipher_encryptv.short
cde47b
+FUNCS += functions/gnutls_aead_cipher_encryptv2
cde47b
+FUNCS += functions/gnutls_aead_cipher_encryptv2.short
cde47b
 FUNCS += functions/gnutls_aead_cipher_init
cde47b
 FUNCS += functions/gnutls_aead_cipher_init.short
cde47b
 FUNCS += functions/gnutls_alert_get
cde47b
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
cde47b
index d06c18013..ee855adf3 100644
cde47b
--- a/doc/manpages/Makefile.am
cde47b
+++ b/doc/manpages/Makefile.am
cde47b
@@ -119,9 +119,11 @@ APIMANS += dane_verify_crt.3
cde47b
 APIMANS += dane_verify_crt_raw.3
cde47b
 APIMANS += dane_verify_session_crt.3
cde47b
 APIMANS += gnutls_aead_cipher_decrypt.3
cde47b
+APIMANS += gnutls_aead_cipher_decryptv2.3
cde47b
 APIMANS += gnutls_aead_cipher_deinit.3
cde47b
 APIMANS += gnutls_aead_cipher_encrypt.3
cde47b
 APIMANS += gnutls_aead_cipher_encryptv.3
cde47b
+APIMANS += gnutls_aead_cipher_encryptv2.3
cde47b
 APIMANS += gnutls_aead_cipher_init.3
cde47b
 APIMANS += gnutls_alert_get.3
cde47b
 APIMANS += gnutls_alert_get_name.3
cde47b
diff --git a/lib/crypto-api.c b/lib/crypto-api.c
cde47b
index 70107fed0..2834c0199 100644
cde47b
--- a/lib/crypto-api.c
cde47b
+++ b/lib/crypto-api.c
cde47b
@@ -885,7 +885,26 @@ static void iov_store_free(struct iov_store_st *s)
cde47b
 	}
cde47b
 }
cde47b
 
cde47b
-static int copy_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
cde47b
+static int iov_store_grow(struct iov_store_st *s, size_t length)
cde47b
+{
cde47b
+	if (s->allocated || s->data == NULL) {
cde47b
+		s->size += length;
cde47b
+		s->data = gnutls_realloc(s->data, s->size);
cde47b
+		if (s->data == NULL)
cde47b
+			return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
cde47b
+		s->allocated = 1;
cde47b
+	} else {
cde47b
+		void *data = s->data;
cde47b
+		size_t size = s->size + length;
cde47b
+		s->data = gnutls_malloc(size);
cde47b
+		memcpy(s->data, data, s->size);
cde47b
+		s->size += length;
cde47b
+	}
cde47b
+	return 0;
cde47b
+}
cde47b
+
cde47b
+static int
cde47b
+copy_from_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
cde47b
 {
cde47b
 	memset(dst, 0, sizeof(*dst));
cde47b
 	if (iovcnt == 0) {
cde47b
@@ -917,6 +936,27 @@ static int copy_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
cde47b
 	}
cde47b
 }
cde47b
 
cde47b
+static int
cde47b
+copy_to_iov(struct iov_store_st *src, size_t size,
cde47b
+	    const giovec_t *iov, int iovcnt)
cde47b
+{
cde47b
+	size_t offset = 0;
cde47b
+	int i;
cde47b
+
cde47b
+	if (unlikely(src->size < size))
cde47b
+		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
cde47b
+
cde47b
+	for (i = 0; i < iovcnt && size > 0; i++) {
cde47b
+		size_t to_copy = MIN(size, iov[i].iov_len);
cde47b
+		memcpy(iov[i].iov_base, (uint8_t *) src->data + offset, to_copy);
cde47b
+		offset += to_copy;
cde47b
+		size -= to_copy;
cde47b
+	}
cde47b
+	if (size > 0)
cde47b
+		return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
cde47b
+	return 0;
cde47b
+}
cde47b
+
cde47b
 
cde47b
 /**
cde47b
  * gnutls_aead_cipher_encryptv:
cde47b
@@ -971,11 +1011,11 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
cde47b
 		struct iov_store_st auth;
cde47b
 		struct iov_store_st ptext;
cde47b
 
cde47b
-		ret = copy_iov(&auth, auth_iov, auth_iovcnt);
cde47b
+		ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
cde47b
 		if (ret < 0)
cde47b
 			return gnutls_assert_val(ret);
cde47b
 
cde47b
-		ret = copy_iov(&ptext, iov, iovcnt);
cde47b
+		ret = copy_from_iov(&ptext, iov, iovcnt);
cde47b
 		if (ret < 0) {
cde47b
 			iov_store_free(&auth);
cde47b
 			return gnutls_assert_val(ret);
cde47b
@@ -1066,6 +1106,316 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
cde47b
 	return 0;
cde47b
 }
cde47b
 
cde47b
+/**
cde47b
+ * gnutls_aead_cipher_encryptv2:
cde47b
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
cde47b
+ * @nonce: the nonce to set
cde47b
+ * @nonce_len: The length of the nonce
cde47b
+ * @auth_iov: additional data to be authenticated
cde47b
+ * @auth_iovcnt: The number of buffers in @auth_iov
cde47b
+ * @iov: the data to be encrypted
cde47b
+ * @iovcnt: The number of buffers in @iov
cde47b
+ * @tag: The authentication tag
cde47b
+ * @tag_size: The size of the tag to use (use zero for the default)
cde47b
+ *
cde47b
+ * This is similar to gnutls_aead_cipher_encrypt(), but it performs
cde47b
+ * in-place encryption on the provided data buffers.
cde47b
+ *
cde47b
+ * Returns: Zero or a negative error code on error.
cde47b
+ *
cde47b
+ * Since: 3.6.10
cde47b
+ **/
cde47b
+int
cde47b
+gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
cde47b
+			     const void *nonce, size_t nonce_len,
cde47b
+			     const giovec_t *auth_iov, int auth_iovcnt,
cde47b
+			     const giovec_t *iov, int iovcnt,
cde47b
+			     void *tag, size_t *tag_size)
cde47b
+{
cde47b
+	api_aead_cipher_hd_st *h = handle;
cde47b
+	ssize_t ret;
cde47b
+	uint8_t *p;
cde47b
+	ssize_t blocksize = handle->ctx_enc.e->blocksize;
cde47b
+	struct iov_iter_st iter;
cde47b
+	size_t blocks;
cde47b
+	size_t _tag_size;
cde47b
+
cde47b
+	if (tag_size == NULL || *tag_size == 0)
cde47b
+		_tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e);
cde47b
+	else
cde47b
+		_tag_size = *tag_size;
cde47b
+
cde47b
+	if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e))
cde47b
+		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
cde47b
+
cde47b
+	/* Limitation: this function provides an optimization under the internally registered
cde47b
+	 * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(),
cde47b
+	 * then this becomes a convenience function as it missed the lower-level primitives
cde47b
+	 * necessary for piecemeal encryption. */
cde47b
+	if (handle->ctx_enc.e->only_aead || handle->ctx_enc.encrypt == NULL) {
cde47b
+		/* ciphertext cannot be produced in a piecemeal approach */
cde47b
+		struct iov_store_st auth;
cde47b
+		struct iov_store_st ptext;
cde47b
+		size_t ptext_size;
cde47b
+
cde47b
+		ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
cde47b
+		if (ret < 0)
cde47b
+			return gnutls_assert_val(ret);
cde47b
+
cde47b
+		ret = copy_from_iov(&ptext, iov, iovcnt);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		ptext_size = ptext.size;
cde47b
+
cde47b
+		/* append space for tag */
cde47b
+		ret = iov_store_grow(&ptext, _tag_size);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		ret = gnutls_aead_cipher_encrypt(handle, nonce, nonce_len,
cde47b
+						 auth.data, auth.size,
cde47b
+						 _tag_size,
cde47b
+						 ptext.data, ptext_size,
cde47b
+						 ptext.data, &ptext.size);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		ret = copy_to_iov(&ptext, ptext_size, iov, iovcnt);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		if (tag != NULL)
cde47b
+			memcpy(tag,
cde47b
+			       (uint8_t *) ptext.data + ptext_size,
cde47b
+			       _tag_size);
cde47b
+		if (tag_size != NULL)
cde47b
+			*tag_size = _tag_size;
cde47b
+
cde47b
+	fallback_fail:
cde47b
+		iov_store_free(&auth);
cde47b
+		iov_store_free(&ptext);
cde47b
+
cde47b
+		return ret;
cde47b
+	}
cde47b
+
cde47b
+	ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+
cde47b
+	ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc, p,
cde47b
+					  blocksize * blocks);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc,
cde47b
+					  iter.block, iter.block_offset);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+
cde47b
+	ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
cde47b
+					      p, blocksize * blocks,
cde47b
+					      p, blocksize * blocks);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
cde47b
+					      iter.block, iter.block_offset,
cde47b
+					      iter.block, iter.block_offset);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+
cde47b
+	if (tag != NULL)
cde47b
+		_gnutls_cipher_tag(&handle->ctx_enc, tag, _tag_size);
cde47b
+	if (tag_size != NULL)
cde47b
+		*tag_size = _tag_size;
cde47b
+
cde47b
+	return 0;
cde47b
+}
cde47b
+
cde47b
+/**
cde47b
+ * gnutls_aead_cipher_decryptv2:
cde47b
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
cde47b
+ * @nonce: the nonce to set
cde47b
+ * @nonce_len: The length of the nonce
cde47b
+ * @auth_iov: additional data to be authenticated
cde47b
+ * @auth_iovcnt: The number of buffers in @auth_iov
cde47b
+ * @iov: the data to decrypt
cde47b
+ * @iovcnt: The number of buffers in @iov
cde47b
+ * @tag: The authentication tag
cde47b
+ * @tag_size: The size of the tag to use (use zero for the default)
cde47b
+ *
cde47b
+ * This is similar to gnutls_aead_cipher_decrypt(), but it performs
cde47b
+ * in-place encryption on the provided data buffers.
cde47b
+ *
cde47b
+ * Returns: Zero or a negative error code on error.
cde47b
+ *
cde47b
+ * Since: 3.6.10
cde47b
+ **/
cde47b
+int
cde47b
+gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
cde47b
+			     const void *nonce, size_t nonce_len,
cde47b
+			     const giovec_t *auth_iov, int auth_iovcnt,
cde47b
+			     const giovec_t *iov, int iovcnt,
cde47b
+			     void *tag, size_t tag_size)
cde47b
+{
cde47b
+	api_aead_cipher_hd_st *h = handle;
cde47b
+	ssize_t ret;
cde47b
+	uint8_t *p;
cde47b
+	ssize_t blocksize = handle->ctx_enc.e->blocksize;
cde47b
+	struct iov_iter_st iter;
cde47b
+	size_t blocks;
cde47b
+	uint8_t _tag[MAX_HASH_SIZE];
cde47b
+
cde47b
+	if (tag_size == 0)
cde47b
+		tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e);
cde47b
+	else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e))
cde47b
+		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
cde47b
+
cde47b
+	/* Limitation: this function provides an optimization under the internally registered
cde47b
+	 * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(),
cde47b
+	 * then this becomes a convenience function as it missed the lower-level primitives
cde47b
+	 * necessary for piecemeal encryption. */
cde47b
+	if (handle->ctx_enc.e->only_aead || handle->ctx_enc.encrypt == NULL) {
cde47b
+		/* ciphertext cannot be produced in a piecemeal approach */
cde47b
+		struct iov_store_st auth;
cde47b
+		struct iov_store_st ctext;
cde47b
+		size_t ctext_size;
cde47b
+
cde47b
+		ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
cde47b
+		if (ret < 0)
cde47b
+			return gnutls_assert_val(ret);
cde47b
+
cde47b
+		ret = copy_from_iov(&ctext, iov, iovcnt);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		ctext_size = ctext.size;
cde47b
+
cde47b
+		/* append tag */
cde47b
+		ret = iov_store_grow(&ctext, tag_size);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+		memcpy((uint8_t *) ctext.data + ctext_size, tag, tag_size);
cde47b
+
cde47b
+		ret = gnutls_aead_cipher_decrypt(handle, nonce, nonce_len,
cde47b
+						 auth.data, auth.size,
cde47b
+						 tag_size,
cde47b
+						 ctext.data, ctext.size,
cde47b
+						 ctext.data, &ctext_size);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+		ret = copy_to_iov(&ctext, ctext_size, iov, iovcnt);
cde47b
+		if (ret < 0) {
cde47b
+			gnutls_assert();
cde47b
+			goto fallback_fail;
cde47b
+		}
cde47b
+
cde47b
+	fallback_fail:
cde47b
+		iov_store_free(&auth);
cde47b
+		iov_store_free(&ctext);
cde47b
+
cde47b
+		return ret;
cde47b
+	}
cde47b
+
cde47b
+	ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+
cde47b
+	ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc, p,
cde47b
+					  blocksize * blocks);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		ret = _gnutls_cipher_auth(&handle->ctx_enc,
cde47b
+					  iter.block, iter.block_offset);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+
cde47b
+	ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
cde47b
+	if (unlikely(ret < 0))
cde47b
+		return gnutls_assert_val(ret);
cde47b
+	while (1) {
cde47b
+		ret = _gnutls_iov_iter_next(&iter, &p);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+		if (ret == 0)
cde47b
+			break;
cde47b
+		blocks = ret;
cde47b
+		ret = _gnutls_cipher_decrypt2(&handle->ctx_enc,
cde47b
+					      p, blocksize * blocks,
cde47b
+					      p, blocksize * blocks);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+	if (iter.block_offset > 0) {
cde47b
+		ret = _gnutls_cipher_decrypt2(&handle->ctx_enc,
cde47b
+					      iter.block, iter.block_offset,
cde47b
+					      iter.block, iter.block_offset);
cde47b
+		if (unlikely(ret < 0))
cde47b
+			return gnutls_assert_val(ret);
cde47b
+	}
cde47b
+
cde47b
+	if (tag != NULL) {
cde47b
+		_gnutls_cipher_tag(&handle->ctx_enc, _tag, tag_size);
cde47b
+		if (gnutls_memcmp(_tag, tag, tag_size) != 0)
cde47b
+			return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
cde47b
+	}
cde47b
+
cde47b
+	return 0;
cde47b
+}
cde47b
+
cde47b
 /**
cde47b
  * gnutls_aead_cipher_deinit:
cde47b
  * @handle: is a #gnutls_aead_cipher_hd_t type.
cde47b
diff --git a/lib/includes/gnutls/crypto.h b/lib/includes/gnutls/crypto.h
cde47b
index d2b8cae8f..4d4926c86 100644
cde47b
--- a/lib/includes/gnutls/crypto.h
cde47b
+++ b/lib/includes/gnutls/crypto.h
cde47b
@@ -92,6 +92,20 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
cde47b
 			    const giovec_t *iov, int iovcnt,
cde47b
 			    void *ctext, size_t *ctext_len);
cde47b
 
cde47b
+int
cde47b
+gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
cde47b
+			     const void *nonce, size_t nonce_len,
cde47b
+			     const giovec_t *auth_iov, int auth_iovcnt,
cde47b
+			     const giovec_t *iov, int iovcnt,
cde47b
+			     void *tag, size_t *tag_size);
cde47b
+
cde47b
+int
cde47b
+gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
cde47b
+			     const void *nonce, size_t nonce_len,
cde47b
+			     const giovec_t *auth_iov, int auth_iovcnt,
cde47b
+			     const giovec_t *iov, int iovcnt,
cde47b
+			     void *tag, size_t tag_size);
cde47b
+
cde47b
 void gnutls_aead_cipher_deinit(gnutls_aead_cipher_hd_t handle);
cde47b
 
cde47b
 /* Hash - MAC API */
cde47b
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
cde47b
index fc93c0857..f83a21e9b 100644
cde47b
--- a/lib/libgnutls.map
cde47b
+++ b/lib/libgnutls.map
cde47b
@@ -1286,6 +1286,13 @@ GNUTLS_3_6_8
cde47b
 	gnutls_ffdhe_8192_group_q;
cde47b
 } GNUTLS_3_6_6;
cde47b
 
cde47b
+GNUTLS_3_6_10
cde47b
+{
cde47b
+ global:
cde47b
+	gnutls_aead_cipher_encryptv2;
cde47b
+	gnutls_aead_cipher_decryptv2;
cde47b
+} GNUTLS_3_6_8;
cde47b
+
cde47b
 GNUTLS_FIPS140_3_4 {
cde47b
   global:
cde47b
 	gnutls_cipher_self_test;
cde47b
diff --git a/tests/Makefile.am b/tests/Makefile.am
cde47b
index a2883570f..075c2728f 100644
cde47b
--- a/tests/Makefile.am
cde47b
+++ b/tests/Makefile.am
cde47b
@@ -212,7 +212,7 @@ ctests += mini-record-2 simple gnutls_hm
cde47b
 	 null_retrieve_function tls-record-size-limit tls-crt_type-neg \
cde47b
 	 resume-with-stek-expiration resume-with-previous-stek rawpk-api \
cde47b
 	 tls-record-size-limit-asym dh-compute ecdh-compute \
cde47b
-	 sign-verify-deterministic iov
cde47b
+	 sign-verify-deterministic iov aead-cipher-vec
cde47b
 
cde47b
 if HAVE_SECCOMP_TESTS
cde47b
 ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
cde47b
diff --git a/tests/aead-cipher-vec.c b/tests/aead-cipher-vec.c
cde47b
new file mode 100644
cde47b
index 000000000..6c2542cf1
cde47b
--- /dev/null
cde47b
+++ b/tests/aead-cipher-vec.c
cde47b
@@ -0,0 +1,123 @@
cde47b
+/*
cde47b
+ * Copyright (C) 2019 Red Hat, Inc.
cde47b
+ *
cde47b
+ * Author: Daiki Ueno
cde47b
+ *
cde47b
+ * This file is part of GnuTLS.
cde47b
+ *
cde47b
+ * GnuTLS is free software; you can redistribute it and/or modify it
cde47b
+ * under the terms of the GNU General Public License as published by
cde47b
+ * the Free Software Foundation; either version 3 of the License, or
cde47b
+ * (at your option) any later version.
cde47b
+ *
cde47b
+ * GnuTLS is distributed in the hope that it will be useful, but
cde47b
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
cde47b
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
cde47b
+ * General Public License for more details.
cde47b
+ *
cde47b
+ * You should have received a copy of the GNU Lesser General Public License
cde47b
+ * along with this program.  If not, see <https://www.gnu.org/licenses/>
cde47b
+ */
cde47b
+
cde47b
+#ifdef HAVE_CONFIG_H
cde47b
+#include <config.h>
cde47b
+#endif
cde47b
+
cde47b
+#include <gnutls/crypto.h>
cde47b
+
cde47b
+#include <assert.h>
cde47b
+#include <stdint.h>
cde47b
+#include <string.h>
cde47b
+#include "utils.h"
cde47b
+
cde47b
+static void tls_log_func(int level, const char *str)
cde47b
+{
cde47b
+	fprintf(stderr, "<%d>| %s", level, str);
cde47b
+}
cde47b
+
cde47b
+/* Test whether gnutls_aead_cipher_{en,de}crypt_vec works */
cde47b
+static void start(const char *name, int algo)
cde47b
+{
cde47b
+	int ret;
cde47b
+	gnutls_aead_cipher_hd_t ch;
cde47b
+	uint8_t key16[64];
cde47b
+	uint8_t iv16[32];
cde47b
+	uint8_t auth[128];
cde47b
+	uint8_t data[128+64];
cde47b
+	gnutls_datum_t key, iv;
cde47b
+	giovec_t iov[2];
cde47b
+	giovec_t auth_iov[2];
cde47b
+	uint8_t tag[64];
cde47b
+	size_t tag_size = 0;
cde47b
+
cde47b
+	key.data = key16;
cde47b
+	key.size = gnutls_cipher_get_key_size(algo);
cde47b
+	assert(key.size <= sizeof(key16));
cde47b
+
cde47b
+	iv.data = iv16;
cde47b
+	iv.size = gnutls_cipher_get_iv_size(algo);
cde47b
+	assert(iv.size <= sizeof(iv16));
cde47b
+
cde47b
+	memset(iv.data, 0xff, iv.size);
cde47b
+	memset(key.data, 0xfe, key.size);
cde47b
+	memset(data, 0xfa, 128);
cde47b
+	memset(auth, 0xaa, sizeof(auth));
cde47b
+
cde47b
+	iov[0].iov_base = data;
cde47b
+	iov[0].iov_len = 64;
cde47b
+	iov[1].iov_base = data + 64;
cde47b
+	iov[1].iov_len = 64;
cde47b
+
cde47b
+	auth_iov[0].iov_base = auth;
cde47b
+	auth_iov[0].iov_len = 64;
cde47b
+	auth_iov[1].iov_base = auth + 64;
cde47b
+	auth_iov[1].iov_len = 64;
cde47b
+
cde47b
+	success("trying %s\n", name);
cde47b
+
cde47b
+	ret =
cde47b
+	    gnutls_aead_cipher_init(&ch, algo, &key);
cde47b
+	if (ret < 0)
cde47b
+		fail("gnutls_cipher_init: %s\n", gnutls_strerror(ret));
cde47b
+
cde47b
+	ret = gnutls_aead_cipher_encryptv2(ch,
cde47b
+					   iv.data, iv.size,
cde47b
+					   auth_iov, 2,
cde47b
+					   iov, 2,
cde47b
+					   tag, &tag_size);
cde47b
+	if (ret < 0)
cde47b
+		fail("could not encrypt data: %s\n", gnutls_strerror(ret));
cde47b
+
cde47b
+	ret = gnutls_aead_cipher_decryptv2(ch,
cde47b
+					   iv.data, iv.size,
cde47b
+					   auth_iov, 2,
cde47b
+					   iov, 2,
cde47b
+					   tag, tag_size);
cde47b
+	if (ret < 0)
cde47b
+		fail("could not decrypt data: %s\n", gnutls_strerror(ret));
cde47b
+
cde47b
+	gnutls_aead_cipher_deinit(ch);
cde47b
+}
cde47b
+
cde47b
+void
cde47b
+doit(void)
cde47b
+{
cde47b
+	int ret;
cde47b
+
cde47b
+	gnutls_global_set_log_function(tls_log_func);
cde47b
+	if (debug)
cde47b
+		gnutls_global_set_log_level(4711);
cde47b
+
cde47b
+	ret = global_init();
cde47b
+	if (ret < 0) {
cde47b
+		fail("Cannot initialize library\n"); /*errcode 1 */
cde47b
+	}
cde47b
+
cde47b
+	start("aes-128-gcm", GNUTLS_CIPHER_AES_128_GCM);
cde47b
+	start("aes-256-gcm", GNUTLS_CIPHER_AES_256_GCM);
cde47b
+	start("aes-128-ccm", GNUTLS_CIPHER_AES_128_CCM);
cde47b
+	if (!gnutls_fips140_mode_enabled())
cde47b
+		start("chacha20-poly1305", GNUTLS_CIPHER_CHACHA20_POLY1305);
cde47b
+
cde47b
+	gnutls_global_deinit();
cde47b
+}
cde47b
-- 
cde47b
2.21.0
cde47b