summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaiki Ueno <dueno@redhat.com>2019-08-02 07:40:44 +0200
committerDaiki Ueno <dueno@redhat.com>2019-08-09 13:11:22 +0200
commitd230011cdbbe55f429b43d818c75c8f6687cbc78 (patch)
treeee01995396ce288a37f3323656a54ae6fecba35d
parent9ca7a2b42168d356126e306e25211d43ea3c2e7d (diff)
downloadgnutls-tmp-encryptv2.tar.gz
crypto-api: add gnutls_aead_cipher_{en,de}cryptv2tmp-encryptv2
This adds an in-place equivalent of gnutls_aead_cipher_encrypt() and gnutls_aead_cipher_decrypt(), that works on data buffers. Signed-off-by: Daiki Ueno <dueno@redhat.com>
-rw-r--r--.gitignore1
-rw-r--r--NEWS7
-rw-r--r--devel/libgnutls-latest-x86_64.abi26
-rw-r--r--devel/symbols.last3
-rw-r--r--doc/Makefile.am4
-rw-r--r--doc/manpages/Makefile.am2
-rw-r--r--lib/crypto-api.c356
-rw-r--r--lib/includes/gnutls/crypto.h14
-rw-r--r--lib/libgnutls.map7
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/aead-cipher-vec.c123
11 files changed, 541 insertions, 4 deletions
diff --git a/.gitignore b/.gitignore
index 26d29fcf34..606257a025 100644
--- a/.gitignore
+++ b/.gitignore
@@ -331,6 +331,7 @@ tags
tests/*/out
tests/Makefile
tests/Makefile.in
+tests/aead-cipher-vec
tests/alerts
tests/alpn-server-prec
tests/anonself
diff --git a/NEWS b/NEWS
index 73a4b6925d..1e3658840d 100644
--- a/NEWS
+++ b/NEWS
@@ -12,6 +12,13 @@ See the end for copying conditions.
GNUTLS_PRIVKEY_FLAG_REPRODUCIBLE when calling gnutls_privkey_sign_*()
functions (#94).
+** libgnutls: add gnutls_aead_cipher_encryptv2 and gnutls_aead_cipher_decryptv2
+ functions that will perform in-place encryption/decryption on data buffers (#718).
+
+** API and ABI modifications:
+gnutls_aead_cipher_encryptv2: Added
+gnutls_aead_cipher_decryptv2: Added
+
* Version 3.6.9 (released 2019-07-25)
** libgnutls: add gnutls_hash_copy/gnutls_hmac_copy functions that will create a copy
diff --git a/devel/libgnutls-latest-x86_64.abi b/devel/libgnutls-latest-x86_64.abi
index 17e8d40663..49044fe938 100644
--- a/devel/libgnutls-latest-x86_64.abi
+++ b/devel/libgnutls-latest-x86_64.abi
@@ -59,9 +59,11 @@
<elf-symbol name='_rsa_generate_fips186_4_keypair' version='GNUTLS_FIPS140_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='dsa_generate_dss_keypair' version='GNUTLS_FIPS140_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_aead_cipher_decrypt' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='gnutls_aead_cipher_decryptv2' version='GNUTLS_3_6_10' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_aead_cipher_deinit' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_aead_cipher_encrypt' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_aead_cipher_encryptv' version='GNUTLS_3_6_3' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
+ <elf-symbol name='gnutls_aead_cipher_encryptv2' version='GNUTLS_3_6_10' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_aead_cipher_init' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_alert_get' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
<elf-symbol name='gnutls_alert_get_name' version='GNUTLS_3_4' is-default-version='yes' type='func-type' binding='global-binding' visibility='default-visibility' is-defined='yes'/>
@@ -8917,6 +8919,30 @@
<parameter type-id='type-id-740' name='handle'/>
<return type-id='type-id-112'/>
</function-decl>
+ <function-decl name='gnutls_aead_cipher_decryptv2' mangled-name='gnutls_aead_cipher_decryptv2' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='gnutls_aead_cipher_decryptv2@@GNUTLS_3_6_10'>
+ <parameter type-id='type-id-740' name='handle'/>
+ <parameter type-id='type-id-102' name='nonce'/>
+ <parameter type-id='type-id-100' name='nonce_len'/>
+ <parameter type-id='type-id-250' name='auth_iov'/>
+ <parameter type-id='type-id-22' name='auth_iovcnt'/>
+ <parameter type-id='type-id-250' name='iov'/>
+ <parameter type-id='type-id-22' name='iovcnt'/>
+ <parameter type-id='type-id-102' name='tag'/>
+ <parameter type-id='type-id-100' name='tag_size'/>
+ <return type-id='type-id-22'/>
+ </function-decl>
+ <function-decl name='gnutls_aead_cipher_encryptv2' mangled-name='gnutls_aead_cipher_encryptv2' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='gnutls_aead_cipher_encryptv2@@GNUTLS_3_6_10'>
+ <parameter type-id='type-id-740' name='handle'/>
+ <parameter type-id='type-id-102' name='nonce'/>
+ <parameter type-id='type-id-100' name='nonce_len'/>
+ <parameter type-id='type-id-250' name='auth_iov'/>
+ <parameter type-id='type-id-22' name='auth_iovcnt'/>
+ <parameter type-id='type-id-250' name='iov'/>
+ <parameter type-id='type-id-22' name='iovcnt'/>
+ <parameter type-id='type-id-102' name='tag'/>
+ <parameter type-id='type-id-423' name='tag_size'/>
+ <return type-id='type-id-22'/>
+ </function-decl>
<function-decl name='gnutls_aead_cipher_encryptv' mangled-name='gnutls_aead_cipher_encryptv' visibility='default' binding='global' size-in-bits='64' elf-symbol-id='gnutls_aead_cipher_encryptv@@GNUTLS_3_6_3'>
<parameter type-id='type-id-740' name='handle'/>
<parameter type-id='type-id-102' name='nonce'/>
diff --git a/devel/symbols.last b/devel/symbols.last
index e5ff89d5e4..730d75043e 100644
--- a/devel/symbols.last
+++ b/devel/symbols.last
@@ -1,5 +1,6 @@
GNUTLS_3_4@GNUTLS_3_4
GNUTLS_3_6_0@GNUTLS_3_6_0
+GNUTLS_3_6_10@GNUTLS_3_6_10
GNUTLS_3_6_2@GNUTLS_3_6_2
GNUTLS_3_6_3@GNUTLS_3_6_3
GNUTLS_3_6_4@GNUTLS_3_6_4
@@ -9,8 +10,10 @@ GNUTLS_3_6_8@GNUTLS_3_6_8
GNUTLS_3_6_9@GNUTLS_3_6_9
_gnutls_global_init_skip@GNUTLS_3_4
gnutls_aead_cipher_decrypt@GNUTLS_3_4
+gnutls_aead_cipher_decryptv2@GNUTLS_3_6_10
gnutls_aead_cipher_deinit@GNUTLS_3_4
gnutls_aead_cipher_encrypt@GNUTLS_3_4
+gnutls_aead_cipher_encryptv2@GNUTLS_3_6_10
gnutls_aead_cipher_encryptv@GNUTLS_3_6_3
gnutls_aead_cipher_init@GNUTLS_3_4
gnutls_alert_get@GNUTLS_3_4
diff --git a/doc/Makefile.am b/doc/Makefile.am
index 6d21d74820..add63c23d5 100644
--- a/doc/Makefile.am
+++ b/doc/Makefile.am
@@ -635,12 +635,16 @@ FUNCS += functions/dane_verify_session_crt
FUNCS += functions/dane_verify_session_crt.short
FUNCS += functions/gnutls_aead_cipher_decrypt
FUNCS += functions/gnutls_aead_cipher_decrypt.short
+FUNCS += functions/gnutls_aead_cipher_decryptv2
+FUNCS += functions/gnutls_aead_cipher_decryptv2.short
FUNCS += functions/gnutls_aead_cipher_deinit
FUNCS += functions/gnutls_aead_cipher_deinit.short
FUNCS += functions/gnutls_aead_cipher_encrypt
FUNCS += functions/gnutls_aead_cipher_encrypt.short
FUNCS += functions/gnutls_aead_cipher_encryptv
FUNCS += functions/gnutls_aead_cipher_encryptv.short
+FUNCS += functions/gnutls_aead_cipher_encryptv2
+FUNCS += functions/gnutls_aead_cipher_encryptv2.short
FUNCS += functions/gnutls_aead_cipher_init
FUNCS += functions/gnutls_aead_cipher_init.short
FUNCS += functions/gnutls_alert_get
diff --git a/doc/manpages/Makefile.am b/doc/manpages/Makefile.am
index d06c180138..ee855adf35 100644
--- a/doc/manpages/Makefile.am
+++ b/doc/manpages/Makefile.am
@@ -119,9 +119,11 @@ APIMANS += dane_verify_crt.3
APIMANS += dane_verify_crt_raw.3
APIMANS += dane_verify_session_crt.3
APIMANS += gnutls_aead_cipher_decrypt.3
+APIMANS += gnutls_aead_cipher_decryptv2.3
APIMANS += gnutls_aead_cipher_deinit.3
APIMANS += gnutls_aead_cipher_encrypt.3
APIMANS += gnutls_aead_cipher_encryptv.3
+APIMANS += gnutls_aead_cipher_encryptv2.3
APIMANS += gnutls_aead_cipher_init.3
APIMANS += gnutls_alert_get.3
APIMANS += gnutls_alert_get_name.3
diff --git a/lib/crypto-api.c b/lib/crypto-api.c
index 70107fed0a..2834c01996 100644
--- a/lib/crypto-api.c
+++ b/lib/crypto-api.c
@@ -885,7 +885,26 @@ static void iov_store_free(struct iov_store_st *s)
}
}
-static int copy_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
+static int iov_store_grow(struct iov_store_st *s, size_t length)
+{
+ if (s->allocated || s->data == NULL) {
+ s->size += length;
+ s->data = gnutls_realloc(s->data, s->size);
+ if (s->data == NULL)
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ s->allocated = 1;
+ } else {
+ void *data = s->data;
+ size_t size = s->size + length;
+ s->data = gnutls_malloc(size);
+ memcpy(s->data, data, s->size);
+ s->size += length;
+ }
+ return 0;
+}
+
+static int
+copy_from_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
{
memset(dst, 0, sizeof(*dst));
if (iovcnt == 0) {
@@ -917,6 +936,27 @@ static int copy_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
}
}
+static int
+copy_to_iov(struct iov_store_st *src, size_t size,
+ const giovec_t *iov, int iovcnt)
+{
+ size_t offset = 0;
+ int i;
+
+ if (unlikely(src->size < size))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ for (i = 0; i < iovcnt && size > 0; i++) {
+ size_t to_copy = MIN(size, iov[i].iov_len);
+ memcpy(iov[i].iov_base, (uint8_t *) src->data + offset, to_copy);
+ offset += to_copy;
+ size -= to_copy;
+ }
+ if (size > 0)
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ return 0;
+}
+
/**
* gnutls_aead_cipher_encryptv:
@@ -971,11 +1011,11 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
struct iov_store_st auth;
struct iov_store_st ptext;
- ret = copy_iov(&auth, auth_iov, auth_iovcnt);
+ ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
if (ret < 0)
return gnutls_assert_val(ret);
- ret = copy_iov(&ptext, iov, iovcnt);
+ ret = copy_from_iov(&ptext, iov, iovcnt);
if (ret < 0) {
iov_store_free(&auth);
return gnutls_assert_val(ret);
@@ -1067,6 +1107,316 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
}
/**
+ * gnutls_aead_cipher_encryptv2:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @nonce: the nonce to set
+ * @nonce_len: The length of the nonce
+ * @auth_iov: additional data to be authenticated
+ * @auth_iovcnt: The number of buffers in @auth_iov
+ * @iov: the data to be encrypted
+ * @iovcnt: The number of buffers in @iov
+ * @tag: The authentication tag
+ * @tag_size: The size of the tag to use (use zero for the default)
+ *
+ * This is similar to gnutls_aead_cipher_encrypt(), but it performs
+ * in-place encryption on the provided data buffers.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.10
+ **/
+int
+gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const giovec_t *auth_iov, int auth_iovcnt,
+ const giovec_t *iov, int iovcnt,
+ void *tag, size_t *tag_size)
+{
+ api_aead_cipher_hd_st *h = handle;
+ ssize_t ret;
+ uint8_t *p;
+ ssize_t blocksize = handle->ctx_enc.e->blocksize;
+ struct iov_iter_st iter;
+ size_t blocks;
+ size_t _tag_size;
+
+ if (tag_size == NULL || *tag_size == 0)
+ _tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e);
+ else
+ _tag_size = *tag_size;
+
+ if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* Limitation: this function provides an optimization under the internally registered
+ * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(),
+ * then this becomes a convenience function as it missed the lower-level primitives
+ * necessary for piecemeal encryption. */
+ if (handle->ctx_enc.e->only_aead || handle->ctx_enc.encrypt == NULL) {
+ /* ciphertext cannot be produced in a piecemeal approach */
+ struct iov_store_st auth;
+ struct iov_store_st ptext;
+ size_t ptext_size;
+
+ ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = copy_from_iov(&ptext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ ptext_size = ptext.size;
+
+ /* append space for tag */
+ ret = iov_store_grow(&ptext, _tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ ret = gnutls_aead_cipher_encrypt(handle, nonce, nonce_len,
+ auth.data, auth.size,
+ _tag_size,
+ ptext.data, ptext_size,
+ ptext.data, &ptext.size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ ret = copy_to_iov(&ptext, ptext_size, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ if (tag != NULL)
+ memcpy(tag,
+ (uint8_t *) ptext.data + ptext_size,
+ _tag_size);
+ if (tag_size != NULL)
+ *tag_size = _tag_size;
+
+ fallback_fail:
+ iov_store_free(&auth);
+ iov_store_free(&ptext);
+
+ return ret;
+ }
+
+ ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ if (ret == 0)
+ break;
+ blocks = ret;
+ ret = _gnutls_cipher_auth(&handle->ctx_enc, p,
+ blocksize * blocks);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+ if (iter.block_offset > 0) {
+ ret = _gnutls_cipher_auth(&handle->ctx_enc,
+ iter.block, iter.block_offset);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ if (ret == 0)
+ break;
+ blocks = ret;
+ ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
+ p, blocksize * blocks,
+ p, blocksize * blocks);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+ if (iter.block_offset > 0) {
+ ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
+ iter.block, iter.block_offset,
+ iter.block, iter.block_offset);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+
+ if (tag != NULL)
+ _gnutls_cipher_tag(&handle->ctx_enc, tag, _tag_size);
+ if (tag_size != NULL)
+ *tag_size = _tag_size;
+
+ return 0;
+}
+
+/**
+ * gnutls_aead_cipher_decryptv2:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @nonce: the nonce to set
+ * @nonce_len: The length of the nonce
+ * @auth_iov: additional data to be authenticated
+ * @auth_iovcnt: The number of buffers in @auth_iov
+ * @iov: the data to decrypt
+ * @iovcnt: The number of buffers in @iov
+ * @tag: The authentication tag
+ * @tag_size: The size of the tag to use (use zero for the default)
+ *
+ * This is similar to gnutls_aead_cipher_decrypt(), but it performs
+ * in-place encryption on the provided data buffers.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.10
+ **/
+int
+gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const giovec_t *auth_iov, int auth_iovcnt,
+ const giovec_t *iov, int iovcnt,
+ void *tag, size_t tag_size)
+{
+ api_aead_cipher_hd_st *h = handle;
+ ssize_t ret;
+ uint8_t *p;
+ ssize_t blocksize = handle->ctx_enc.e->blocksize;
+ struct iov_iter_st iter;
+ size_t blocks;
+ uint8_t _tag[MAX_HASH_SIZE];
+
+ if (tag_size == 0)
+ tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e);
+ else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e))
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ /* Limitation: this function provides an optimization under the internally registered
+ * AEAD ciphers. When an AEAD cipher is used registered with gnutls_crypto_register_aead_cipher(),
+ * then this becomes a convenience function as it missed the lower-level primitives
+ * necessary for piecemeal encryption. */
+ if (handle->ctx_enc.e->only_aead || handle->ctx_enc.encrypt == NULL) {
+ /* ciphertext cannot be produced in a piecemeal approach */
+ struct iov_store_st auth;
+ struct iov_store_st ctext;
+ size_t ctext_size;
+
+ ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
+ ret = copy_from_iov(&ctext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ ctext_size = ctext.size;
+
+ /* append tag */
+ ret = iov_store_grow(&ctext, tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+ memcpy((uint8_t *) ctext.data + ctext_size, tag, tag_size);
+
+ ret = gnutls_aead_cipher_decrypt(handle, nonce, nonce_len,
+ auth.data, auth.size,
+ tag_size,
+ ctext.data, ctext.size,
+ ctext.data, &ctext_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ ret = copy_to_iov(&ctext, ctext_size, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto fallback_fail;
+ }
+
+ fallback_fail:
+ iov_store_free(&auth);
+ iov_store_free(&ctext);
+
+ return ret;
+ }
+
+ ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+
+ ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ if (ret == 0)
+ break;
+ blocks = ret;
+ ret = _gnutls_cipher_auth(&handle->ctx_enc, p,
+ blocksize * blocks);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+ if (iter.block_offset > 0) {
+ ret = _gnutls_cipher_auth(&handle->ctx_enc,
+ iter.block, iter.block_offset);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ if (ret == 0)
+ break;
+ blocks = ret;
+ ret = _gnutls_cipher_decrypt2(&handle->ctx_enc,
+ p, blocksize * blocks,
+ p, blocksize * blocks);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+ if (iter.block_offset > 0) {
+ ret = _gnutls_cipher_decrypt2(&handle->ctx_enc,
+ iter.block, iter.block_offset,
+ iter.block, iter.block_offset);
+ if (unlikely(ret < 0))
+ return gnutls_assert_val(ret);
+ }
+
+ if (tag != NULL) {
+ _gnutls_cipher_tag(&handle->ctx_enc, _tag, tag_size);
+ if (gnutls_memcmp(_tag, tag, tag_size) != 0)
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ return 0;
+}
+
+/**
* gnutls_aead_cipher_deinit:
* @handle: is a #gnutls_aead_cipher_hd_t type.
*
diff --git a/lib/includes/gnutls/crypto.h b/lib/includes/gnutls/crypto.h
index d2b8cae8f4..4d4926c86a 100644
--- a/lib/includes/gnutls/crypto.h
+++ b/lib/includes/gnutls/crypto.h
@@ -92,6 +92,20 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
const giovec_t *iov, int iovcnt,
void *ctext, size_t *ctext_len);
+int
+gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const giovec_t *auth_iov, int auth_iovcnt,
+ const giovec_t *iov, int iovcnt,
+ void *tag, size_t *tag_size);
+
+int
+gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const giovec_t *auth_iov, int auth_iovcnt,
+ const giovec_t *iov, int iovcnt,
+ void *tag, size_t tag_size);
+
void gnutls_aead_cipher_deinit(gnutls_aead_cipher_hd_t handle);
/* Hash - MAC API */
diff --git a/lib/libgnutls.map b/lib/libgnutls.map
index fc93c0857f..f83a21e9bc 100644
--- a/lib/libgnutls.map
+++ b/lib/libgnutls.map
@@ -1294,6 +1294,13 @@ GNUTLS_3_6_9
gnutls_hash_copy;
} GNUTLS_3_6_8;
+GNUTLS_3_6_10
+{
+ global:
+ gnutls_aead_cipher_encryptv2;
+ gnutls_aead_cipher_decryptv2;
+} GNUTLS_3_6_9;
+
GNUTLS_FIPS140_3_4 {
global:
gnutls_cipher_self_test;
diff --git a/tests/Makefile.am b/tests/Makefile.am
index a2883570f3..075c2728f3 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -214,7 +214,7 @@ ctests += mini-record-2 simple gnutls_hmac_fast set_pkcs12_cred cert certuniquei
null_retrieve_function tls-record-size-limit tls-crt_type-neg \
resume-with-stek-expiration resume-with-previous-stek rawpk-api \
tls-record-size-limit-asym dh-compute ecdh-compute sign-verify-data-newapi \
- sign-verify-newapi sign-verify-deterministic iov
+ sign-verify-newapi sign-verify-deterministic iov aead-cipher-vec
if HAVE_SECCOMP_TESTS
ctests += dtls-with-seccomp tls-with-seccomp dtls-client-with-seccomp tls-client-with-seccomp
diff --git a/tests/aead-cipher-vec.c b/tests/aead-cipher-vec.c
new file mode 100644
index 0000000000..6c2542cf10
--- /dev/null
+++ b/tests/aead-cipher-vec.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 Red Hat, Inc.
+ *
+ * Author: Daiki Ueno
+ *
+ * This file is part of GnuTLS.
+ *
+ * GnuTLS is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuTLS 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
+ * 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 <https://www.gnu.org/licenses/>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <gnutls/crypto.h>
+
+#include <assert.h>
+#include <stdint.h>
+#include <string.h>
+#include "utils.h"
+
+static void tls_log_func(int level, const char *str)
+{
+ fprintf(stderr, "<%d>| %s", level, str);
+}
+
+/* Test whether gnutls_aead_cipher_{en,de}crypt_vec works */
+static void start(const char *name, int algo)
+{
+ int ret;
+ gnutls_aead_cipher_hd_t ch;
+ uint8_t key16[64];
+ uint8_t iv16[32];
+ uint8_t auth[128];
+ uint8_t data[128+64];
+ gnutls_datum_t key, iv;
+ giovec_t iov[2];
+ giovec_t auth_iov[2];
+ uint8_t tag[64];
+ size_t tag_size = 0;
+
+ key.data = key16;
+ key.size = gnutls_cipher_get_key_size(algo);
+ assert(key.size <= sizeof(key16));
+
+ iv.data = iv16;
+ iv.size = gnutls_cipher_get_iv_size(algo);
+ assert(iv.size <= sizeof(iv16));
+
+ memset(iv.data, 0xff, iv.size);
+ memset(key.data, 0xfe, key.size);
+ memset(data, 0xfa, 128);
+ memset(auth, 0xaa, sizeof(auth));
+
+ iov[0].iov_base = data;
+ iov[0].iov_len = 64;
+ iov[1].iov_base = data + 64;
+ iov[1].iov_len = 64;
+
+ auth_iov[0].iov_base = auth;
+ auth_iov[0].iov_len = 64;
+ auth_iov[1].iov_base = auth + 64;
+ auth_iov[1].iov_len = 64;
+
+ success("trying %s\n", name);
+
+ ret =
+ gnutls_aead_cipher_init(&ch, algo, &key);
+ if (ret < 0)
+ fail("gnutls_cipher_init: %s\n", gnutls_strerror(ret));
+
+ ret = gnutls_aead_cipher_encryptv2(ch,
+ iv.data, iv.size,
+ auth_iov, 2,
+ iov, 2,
+ tag, &tag_size);
+ if (ret < 0)
+ fail("could not encrypt data: %s\n", gnutls_strerror(ret));
+
+ ret = gnutls_aead_cipher_decryptv2(ch,
+ iv.data, iv.size,
+ auth_iov, 2,
+ iov, 2,
+ tag, tag_size);
+ if (ret < 0)
+ fail("could not decrypt data: %s\n", gnutls_strerror(ret));
+
+ gnutls_aead_cipher_deinit(ch);
+}
+
+void
+doit(void)
+{
+ int ret;
+
+ gnutls_global_set_log_function(tls_log_func);
+ if (debug)
+ gnutls_global_set_log_level(4711);
+
+ ret = global_init();
+ if (ret < 0) {
+ fail("Cannot initialize library\n"); /*errcode 1 */
+ }
+
+ start("aes-128-gcm", GNUTLS_CIPHER_AES_128_GCM);
+ start("aes-256-gcm", GNUTLS_CIPHER_AES_256_GCM);
+ start("aes-128-ccm", GNUTLS_CIPHER_AES_128_CCM);
+ if (!gnutls_fips140_mode_enabled())
+ start("chacha20-poly1305", GNUTLS_CIPHER_CHACHA20_POLY1305);
+
+ gnutls_global_deinit();
+}