]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
crypto-api: split scatter-gather AEAD implementation to helper funcs
authorDaiki Ueno <ueno@gnu.org>
Mon, 17 Jan 2022 09:36:44 +0000 (10:36 +0100)
committerDaiki Ueno <ueno@gnu.org>
Thu, 5 May 2022 14:43:31 +0000 (16:43 +0200)
These _encryptv, _encryptv2, and _decryptv2 functions take orthogonal
code paths depending on whether the underlying AEAD implementation
supports message based API.  This patch split the implementation to
dedicated helper functions.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
lib/crypto-api.c

index 85c58169bb81bbf363d018617b657f12bedad842..0b352a6d28a7adc12ab67a906e9f5c1feaac4991 100644 (file)
@@ -1198,38 +1198,59 @@ copy_to_iov(struct iov_store_st *src, size_t size,
        return 0;
 }
 
+static int
+aead_cipher_encryptv_fallback(gnutls_aead_cipher_hd_t handle,
+                             const void *nonce, size_t nonce_len,
+                             const giovec_t *auth_iov, int auth_iovcnt,
+                             size_t tag_size,
+                             const giovec_t *iov, int iovcnt,
+                             void *ctext, size_t *ctext_len)
+{
+       struct iov_store_st auth;
+       struct iov_store_st ptext;
+       int ret;
 
-/**
- * gnutls_aead_cipher_encryptv:
- * @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
- * @tag_size: The size of the tag to use (use zero for the default)
- * @iov: the data to be encrypted
- * @iovcnt: The number of buffers in @iov
- * @ctext: the encrypted data including authentication tag
- * @ctext_len: the length of encrypted data (initially must hold the maximum available size, including space for tag)
- *
- * This function will encrypt the provided data buffers using the algorithm
- * specified by the context. The output data will contain the
- * authentication tag.
- *
- * Returns: Zero or a negative error code on error.
- *
- * Since: 3.6.3
- **/
-int
-gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
-                           const void *nonce, size_t nonce_len,
-                           const giovec_t *auth_iov, int auth_iovcnt,
-                           size_t tag_size,
-                           const giovec_t *iov, int iovcnt,
-                           void *ctext, size_t *ctext_len)
+       if (tag_size == 0)
+               tag_size = _gnutls_cipher_get_tag_size(handle->ctx_enc.e);
+       else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(handle->ctx_enc.e)) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+
+       ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
+       if (ret < 0) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(ret);
+       }
+
+       ret = copy_from_iov(&ptext, iov, iovcnt);
+       if (ret < 0) {
+               iov_store_free(&auth);
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(ret);
+       }
+
+       ret = gnutls_aead_cipher_encrypt(handle, nonce, nonce_len,
+                                        auth.data, auth.size,
+                                        tag_size,
+                                        ptext.data, ptext.size,
+                                        ctext, ctext_len);
+       iov_store_free(&auth);
+       iov_store_free(&ptext);
+
+       /* FIPS operation state is set by gnutls_aead_cipher_encrypt */
+       return ret;
+}
+
+static int
+aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
+                    const void *nonce, size_t nonce_len,
+                    const giovec_t *auth_iov, int auth_iovcnt,
+                    size_t tag_size,
+                    const giovec_t *iov, int iovcnt,
+                    void *ctext, size_t *ctext_len)
 {
-       api_aead_cipher_hd_st *h = handle;
-       ssize_t ret;
+       int ret;
        uint8_t *dst;
        size_t dst_size, total = 0;
        uint8_t *p;
@@ -1237,48 +1258,13 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
        size_t blocksize = handle->ctx_enc.e->blocksize;
        struct iov_iter_st iter;
 
-       /* 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 (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)) {
+               tag_size = _gnutls_cipher_get_tag_size(handle->ctx_enc.e);
+       else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(handle->ctx_enc.e)) {
                _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
        }
 
-       if ((handle->ctx_enc.e->flags & GNUTLS_CIPHER_FLAG_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;
-
-               ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
-               if (ret < 0) {
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-                       return gnutls_assert_val(ret);
-               }
-
-               ret = copy_from_iov(&ptext, iov, iovcnt);
-               if (ret < 0) {
-                       iov_store_free(&auth);
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-                       return gnutls_assert_val(ret);
-               }
-
-               ret = gnutls_aead_cipher_encrypt(handle, nonce, nonce_len,
-                                                auth.data, auth.size,
-                                                tag_size,
-                                                ptext.data, ptext.size,
-                                                ctext, ctext_len);
-               iov_store_free(&auth);
-               iov_store_free(&ptext);
-
-               /* FIPS operation state is set by gnutls_aead_cipher_encrypt */
-               return ret;
-       }
-
        ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
        if (unlikely(ret < 0)) {
                _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
@@ -1350,112 +1336,157 @@ gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
 }
 
 /**
- * gnutls_aead_cipher_encryptv2:
+ * gnutls_aead_cipher_encryptv:
  * @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
+ * @tag_size: The size of the tag to use (use zero for the default)
  * @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)
+ * @ctext: the encrypted data including authentication tag
+ * @ctext_len: the length of encrypted data (initially must hold the maximum available size, including space for tag)
  *
- * This is similar to gnutls_aead_cipher_encrypt(), but it performs
- * in-place encryption on the provided data buffers.
+ * This function will encrypt the provided data buffers using the algorithm
+ * specified by the context. The output data will contain the
+ * authentication tag.
  *
  * Returns: Zero or a negative error code on error.
  *
- * Since: 3.6.10
+ * Since: 3.6.3
  **/
 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)
+gnutls_aead_cipher_encryptv(gnutls_aead_cipher_hd_t handle,
+                           const void *nonce, size_t nonce_len,
+                           const giovec_t *auth_iov, int auth_iovcnt,
+                           size_t tag_size,
+                           const giovec_t *iov, int iovcnt,
+                           void *ctext, size_t *ctext_len)
+{
+       /* 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->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD) ||
+           handle->ctx_enc.encrypt == NULL) {
+               return aead_cipher_encryptv_fallback(handle,
+                                                    nonce, nonce_len,
+                                                    auth_iov, auth_iovcnt,
+                                                    tag_size,
+                                                    iov, iovcnt,
+                                                    ctext, ctext_len);
+       } else {
+               return aead_cipher_encryptv(handle,
+                                           nonce, nonce_len,
+                                           auth_iov, auth_iovcnt,
+                                           tag_size,
+                                           iov, iovcnt,
+                                           ctext, ctext_len);
+       }
+}
+
+static int
+aead_cipher_encryptv2_fallback(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;
-       size_t len;
-       ssize_t blocksize = handle->ctx_enc.e->blocksize;
-       struct iov_iter_st iter;
        size_t _tag_size;
+       struct iov_store_st auth;
+       struct iov_store_st ptext;
+       size_t ptext_size;
+       int ret;
 
        if (tag_size == NULL || *tag_size == 0)
-               _tag_size = _gnutls_cipher_get_tag_size(h->ctx_enc.e);
+               _tag_size = _gnutls_cipher_get_tag_size(handle->ctx_enc.e);
        else
                _tag_size = *tag_size;
 
-       if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(h->ctx_enc.e)) {
+       if (_tag_size > (unsigned)_gnutls_cipher_get_tag_size(handle->ctx_enc.e)) {
                _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
                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->flags & GNUTLS_CIPHER_FLAG_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) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(ret);
+       }
 
-               ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
-               if (ret < 0) {
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-                       return gnutls_assert_val(ret);
-               }
+       ret = copy_from_iov(&ptext, iov, iovcnt);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
 
-               ret = copy_from_iov(&ptext, iov, iovcnt);
-               if (ret < 0) {
-                       gnutls_assert();
-                       goto fallback_fail;
-               }
+       ptext_size = ptext.size;
 
-               ptext_size = ptext.size;
+       /* append space for tag */
+       ret = iov_store_grow(&ptext, _tag_size);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
 
-               /* 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 error;
+       }
 
-               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 error;
+       }
 
-               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;
 
-               if (tag != NULL)
-                       memcpy(tag,
-                              (uint8_t *) ptext.data + ptext_size,
-                              _tag_size);
-               if (tag_size != NULL)
-                       *tag_size = _tag_size;
+ error:
+       iov_store_free(&auth);
+       iov_store_free(&ptext);
 
-       fallback_fail:
-               iov_store_free(&auth);
-               iov_store_free(&ptext);
+       if (ret < 0) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+       }
+       /* FIPS operation state is set by gnutls_aead_cipher_encrypt */
+       return ret;
+}
 
-               if (ret < 0) {
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-               }
-               /* FIPS operation state is set by gnutls_aead_cipher_encrypt */
-               return ret;
+static int
+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;
+       int ret;
+       uint8_t *p;
+       size_t len;
+       size_t blocksize = handle->ctx_enc.e->blocksize;
+       struct iov_iter_st iter;
+       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)) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
        }
 
        ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
@@ -1520,18 +1551,18 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
 }
 
 /**
- * gnutls_aead_cipher_decryptv2:
+ * 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 decrypt
+ * @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_decrypt(), but it performs
+ * 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.
@@ -1539,84 +1570,119 @@ gnutls_aead_cipher_encryptv2(gnutls_aead_cipher_hd_t handle,
  * Since: 3.6.10
  **/
 int
-gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
+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)
+                            void *tag, size_t *tag_size)
 {
-       api_aead_cipher_hd_st *h = handle;
-       ssize_t ret;
-       uint8_t *p;
-       size_t len;
-       ssize_t blocksize = handle->ctx_enc.e->blocksize;
-       struct iov_iter_st iter;
-       uint8_t _tag[MAX_HASH_SIZE];
+       /* 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->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD) ||
+           handle->ctx_enc.encrypt == NULL) {
+               return aead_cipher_encryptv2_fallback(handle,
+                                                     nonce, nonce_len,
+                                                     auth_iov, auth_iovcnt,
+                                                     iov, iovcnt,
+                                                     tag, tag_size);
+       } else {
+               return aead_cipher_encryptv2(handle,
+                                            nonce, nonce_len,
+                                            auth_iov, auth_iovcnt,
+                                            iov, iovcnt,
+                                            tag, tag_size);
+       }
+}
+
+static int
+aead_cipher_decryptv2_fallback(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)
+{
+       struct iov_store_st auth;
+       struct iov_store_st ctext;
+       size_t ctext_size;
+       int ret;
 
        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)) {
+               tag_size = _gnutls_cipher_get_tag_size(handle->ctx_enc.e);
+       else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(handle->ctx_enc.e)) {
                _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
                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->flags & GNUTLS_CIPHER_FLAG_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) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(ret);
+       }
 
-               ret = copy_from_iov(&auth, auth_iov, auth_iovcnt);
-               if (ret < 0) {
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-                       return gnutls_assert_val(ret);
-               }
+       ret = copy_from_iov(&ctext, iov, iovcnt);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
 
-               ret = copy_from_iov(&ctext, iov, iovcnt);
-               if (ret < 0) {
-                       gnutls_assert();
-                       goto fallback_fail;
-               }
+       ctext_size = ctext.size;
 
-               ctext_size = ctext.size;
+       /* append tag */
+       ret = iov_store_grow(&ctext, tag_size);
+       if (ret < 0) {
+               gnutls_assert();
+               goto error;
+       }
+       memcpy((uint8_t *) ctext.data + ctext_size, tag, tag_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 error;
+       }
 
-               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 error;
+       }
 
-               ret = copy_to_iov(&ctext, ctext_size, iov, iovcnt);
-               if (ret < 0) {
-                       gnutls_assert();
-                       goto fallback_fail;
-               }
+ error:
+       iov_store_free(&auth);
+       iov_store_free(&ctext);
 
-       fallback_fail:
-               iov_store_free(&auth);
-               iov_store_free(&ctext);
+       if (ret < 0) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+       }
+       /* FIPS operation state is set by gnutls_aead_cipher_decrypt */
+       return ret;
+}
 
-               if (ret < 0) {
-                       _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
-               }
-               /* FIPS operation state is set by gnutls_aead_cipher_decrypt */
-               return ret;
+static int
+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)
+{
+       int ret;
+       uint8_t *p;
+       size_t len;
+       ssize_t blocksize = handle->ctx_enc.e->blocksize;
+       struct iov_iter_st iter;
+       uint8_t _tag[MAX_HASH_SIZE];
+
+       if (tag_size == 0)
+               tag_size = _gnutls_cipher_get_tag_size(handle->ctx_enc.e);
+       else if (tag_size > (unsigned)_gnutls_cipher_get_tag_size(handle->ctx_enc.e)) {
+               _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
        }
 
        ret = _gnutls_cipher_setiv(&handle->ctx_enc, nonce, nonce_len);
@@ -1685,6 +1751,52 @@ gnutls_aead_cipher_decryptv2(gnutls_aead_cipher_hd_t handle,
        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)
+{
+       /* 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->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD) ||
+           handle->ctx_enc.encrypt == NULL) {
+               return aead_cipher_decryptv2_fallback(handle,
+                                                     nonce, nonce_len,
+                                                     auth_iov, auth_iovcnt,
+                                                     iov, iovcnt,
+                                                     tag, tag_size);
+       } else {
+               return aead_cipher_decryptv2(handle,
+                                            nonce, nonce_len,
+                                            auth_iov, auth_iovcnt,
+                                            iov, iovcnt,
+                                            tag, tag_size);
+       }
+}
+
 /**
  * gnutls_aead_cipher_deinit:
  * @handle: is a #gnutls_aead_cipher_hd_t type.