summaryrefslogtreecommitdiffstats
path: root/lib/crypto-api.c
diff options
context:
space:
mode:
Diffstat (limited to 'lib/crypto-api.c')
-rw-r--r--lib/crypto-api.c2232
1 files changed, 2232 insertions, 0 deletions
diff --git a/lib/crypto-api.c b/lib/crypto-api.c
new file mode 100644
index 0000000..7f81011
--- /dev/null
+++ b/lib/crypto-api.c
@@ -0,0 +1,2232 @@
+/*
+ * Copyright (C) 2000-2016 Free Software Foundation, Inc.
+ * Copyright (C) 2016 Red Hat, Inc.
+ *
+ * Author: Nikos Mavrogiannopoulos
+ *
+ * This file is part of GnuTLS.
+ *
+ * The GnuTLS is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * as published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program. If not, see <https://www.gnu.org/licenses/>
+ *
+ */
+
+#include "gnutls_int.h"
+#include "errors.h"
+#include <cipher_int.h>
+#include <datum.h>
+#include <gnutls/crypto.h>
+#include <algorithms.h>
+#include <random.h>
+#include <crypto.h>
+#include <fips.h>
+#include "crypto-api.h"
+#include "iov.h"
+#include "intprops.h"
+
+typedef struct api_cipher_hd_st {
+ cipher_hd_st ctx_enc;
+ cipher_hd_st ctx_dec;
+} api_cipher_hd_st;
+
+/**
+ * gnutls_cipher_init:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @cipher: the encryption algorithm to use
+ * @key: the key to be used for encryption/decryption
+ * @iv: the IV to use (if not applicable set NULL)
+ *
+ * This function will initialize the @handle context to be usable
+ * for encryption/decryption of data. This will effectively use the
+ * current crypto backend in use by gnutls or the cryptographic
+ * accelerator in use.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_cipher_init(gnutls_cipher_hd_t * handle,
+ gnutls_cipher_algorithm_t cipher,
+ const gnutls_datum_t * key, const gnutls_datum_t * iv)
+{
+ api_cipher_hd_st *h;
+ int ret;
+ const cipher_entry_st* e;
+ bool not_approved = false;
+
+ if (!is_cipher_algo_allowed(cipher)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_cipher_algo_approved_in_fips(cipher)) {
+ not_approved = true;
+ }
+
+ e = cipher_to_entry(cipher);
+ if (e == NULL || (e->flags & GNUTLS_CIPHER_FLAG_ONLY_AEAD)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ h = gnutls_calloc(1, sizeof(api_cipher_hd_st));
+ if (h == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret =
+ _gnutls_cipher_init(&h->ctx_enc, e, key,
+ iv, 1);
+ if (ret < 0) {
+ gnutls_free(h);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return ret;
+ }
+
+ if (_gnutls_cipher_type(e) == CIPHER_BLOCK) {
+ ret =
+ _gnutls_cipher_init(&h->ctx_dec, e, key, iv, 0);
+ if (ret < 0) {
+ gnutls_free(h);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return ret;
+ }
+ }
+
+ *handle = h;
+
+ if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_cipher_tag:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @tag: will hold the tag
+ * @tag_size: the length of the tag to return
+ *
+ * This function operates on authenticated encryption with
+ * associated data (AEAD) ciphers and will return the
+ * output tag.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_cipher_tag(gnutls_cipher_hd_t handle, void *tag, size_t tag_size)
+{
+ api_cipher_hd_st *h = handle;
+
+ if (_gnutls_cipher_is_aead(&h->ctx_enc) == 0)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+
+ _gnutls_cipher_tag(&h->ctx_enc, tag, tag_size);
+
+ return 0;
+}
+
+/**
+ * gnutls_cipher_add_auth:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ptext: the data to be authenticated
+ * @ptext_size: the length of the data
+ *
+ * This function operates on authenticated encryption with
+ * associated data (AEAD) ciphers and authenticate the
+ * input data. This function can only be called once
+ * and before any encryption operations.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.0
+ **/
+int
+gnutls_cipher_add_auth(gnutls_cipher_hd_t handle, const void *ptext,
+ size_t ptext_size)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ if (_gnutls_cipher_is_aead(&h->ctx_enc) == 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ ret = _gnutls_cipher_auth(&h->ctx_enc, ptext, ptext_size);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_set_iv:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @iv: the IV to set
+ * @ivlen: the length of the IV
+ *
+ * This function will set the IV to be used for the next
+ * encryption block.
+ *
+ * Since: 3.0
+ **/
+void
+gnutls_cipher_set_iv(gnutls_cipher_hd_t handle, void *iv, size_t ivlen)
+{
+ api_cipher_hd_st *h = handle;
+
+ if (_gnutls_cipher_setiv(&h->ctx_enc, iv, ivlen) < 0) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+
+ if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK) {
+ if (_gnutls_cipher_setiv(&h->ctx_dec, iv, ivlen) < 0) {
+ _gnutls_switch_lib_state(LIB_STATE_ERROR);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+ }
+}
+
+/*-
+ * _gnutls_cipher_get_iv:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @iv: the IV to set
+ * @ivlen: the length of the IV
+ *
+ * This function will retrieve the internally calculated IV value. It is
+ * intended to be used for modes like CFB. @iv must have @ivlen length
+ * at least.
+ *
+ * This is solely for validation purposes of our crypto
+ * implementation. For other purposes, the IV can be typically
+ * calculated from the initial IV value and the subsequent ciphertext
+ * values. As such, this function only works with the internally
+ * registered ciphers.
+ *
+ * Returns: The length of IV or a negative error code on error.
+ *
+ * Since: 3.6.8
+ -*/
+int
+_gnutls_cipher_get_iv(gnutls_cipher_hd_t handle, void *iv, size_t ivlen)
+{
+ api_cipher_hd_st *h = handle;
+
+ return _gnutls_cipher_getiv(&h->ctx_enc, iv, ivlen);
+}
+
+/*-
+ * _gnutls_cipher_set_key:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @key: the key to set
+ * @keylen: the length of the key
+ *
+ * This function will set the key used by the cipher
+ *
+ * This is solely for validation purposes of our crypto
+ * implementation. For other purposes, the key should be set at the time of
+ * cipher setup. As such, this function only works with the internally
+ * registered ciphers.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.14
+ -*/
+int
+_gnutls_cipher_set_key(gnutls_cipher_hd_t handle, void *key, size_t keylen)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ ret = _gnutls_cipher_setkey(&h->ctx_enc, key, keylen);
+
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_encrypt:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ptext: the data to encrypt
+ * @ptext_len: the length of data to encrypt
+ *
+ * This function will encrypt the given data using the algorithm
+ * specified by the context.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_cipher_encrypt(gnutls_cipher_hd_t handle, void *ptext,
+ size_t ptext_len)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ ret = _gnutls_cipher_encrypt(&h->ctx_enc, ptext, ptext_len);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_decrypt:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ctext: the data to decrypt
+ * @ctext_len: the length of data to decrypt
+ *
+ * This function will decrypt the given data using the algorithm
+ * specified by the context.
+ *
+ * Note that in AEAD ciphers, this will not check the tag. You will
+ * need to compare the tag sent with the value returned from gnutls_cipher_tag().
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_cipher_decrypt(gnutls_cipher_hd_t handle, void *ctext,
+ size_t ctext_len)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) {
+ ret = _gnutls_cipher_decrypt(&h->ctx_enc, ctext,
+ ctext_len);
+ } else {
+ ret = _gnutls_cipher_decrypt(&h->ctx_dec, ctext,
+ ctext_len);
+ }
+
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_encrypt2:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ptext: the data to encrypt
+ * @ptext_len: the length of data to encrypt
+ * @ctext: the encrypted data
+ * @ctext_len: the available length for encrypted data
+ *
+ * This function will encrypt the given data using the algorithm
+ * specified by the context. For block ciphers the @ptext_len must be
+ * a multiple of the block size. For the supported ciphers the encrypted
+ * data length will equal the plaintext size.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_cipher_encrypt2(gnutls_cipher_hd_t handle, const void *ptext,
+ size_t ptext_len, void *ctext,
+ size_t ctext_len)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ ret = _gnutls_cipher_encrypt2(&h->ctx_enc, ptext, ptext_len,
+ ctext, ctext_len);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_decrypt2:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ctext: the data to decrypt
+ * @ctext_len: the length of data to decrypt
+ * @ptext: the decrypted data
+ * @ptext_len: the available length for decrypted data
+ *
+ * This function will decrypt the given data using the algorithm
+ * specified by the context. For block ciphers the @ctext_len must be
+ * a multiple of the block size. For the supported ciphers the plaintext
+ * data length will equal the ciphertext size.
+ *
+ * Note that in AEAD ciphers, this will not check the tag. You will
+ * need to compare the tag sent with the value returned from gnutls_cipher_tag().
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.12.0
+ **/
+int
+gnutls_cipher_decrypt2(gnutls_cipher_hd_t handle, const void *ctext,
+ size_t ctext_len, void *ptext, size_t ptext_len)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ if (_gnutls_cipher_type(h->ctx_enc.e) != CIPHER_BLOCK) {
+ ret = _gnutls_cipher_decrypt2(&h->ctx_enc, ctext,
+ ctext_len, ptext,
+ ptext_len);
+ } else {
+ ret = _gnutls_cipher_decrypt2(&h->ctx_dec, ctext,
+ ctext_len, ptext,
+ ptext_len);
+ }
+
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_encrypt3:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ptext: the data to encrypt
+ * @ptext_len: the length of data to encrypt
+ * @ctext: the encrypted data
+ * @ctext_len: the length of encrypted data (initially must hold the maximum available size)
+ * @flags: flags for padding
+ *
+ * This function will encrypt the given data using the algorithm
+ * specified by the context. For block ciphers, @ptext_len is
+ * typically a multiple of the block size. If not, the caller can
+ * instruct the function to pad the last block according to @flags.
+ * Currently, the only available padding scheme is
+ * %GNUTLS_CIPHER_PADDING_PKCS7.
+ *
+ * If @ctext is not %NULL, it must hold enough space to store
+ * resulting cipher text. To check the required size, this function
+ * can be called with @ctext set to %NULL. Then @ctext_len will be
+ * updated without performing actual encryption.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.7.7
+ **/
+int
+gnutls_cipher_encrypt3(gnutls_cipher_hd_t handle,
+ const void *ptext, size_t ptext_len,
+ void *ctext, size_t *ctext_len,
+ unsigned flags)
+{
+ api_cipher_hd_st *h = handle;
+ const cipher_entry_st *e = h->ctx_enc.e;
+ int block_size = _gnutls_cipher_get_block_size(e);
+ int ret = 0;
+
+ if (unlikely(ctext_len == NULL)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if (_gnutls_cipher_type(e) == CIPHER_BLOCK &&
+ (flags & GNUTLS_CIPHER_PADDING_PKCS7)) {
+ size_t n, r;
+ uint8_t last_block[MAX_CIPHER_BLOCK_SIZE];
+ const uint8_t *p = ptext;
+ uint8_t *c = ctext;
+
+ if (!INT_ADD_OK(ptext_len, block_size, &n)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ n = (n / block_size) * block_size;
+
+ if (!ctext) {
+ *ctext_len = n;
+ return 0;
+ }
+
+ if (*ctext_len < n) {
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ }
+
+ /* Encrypt up to the last complete block */
+ r = ptext_len % block_size;
+
+ ret = _gnutls_cipher_encrypt2(&h->ctx_enc,
+ ptext, ptext_len - r,
+ ctext, ptext_len - r);
+ if (ret < 0) {
+ goto error;
+ }
+
+ /* Encrypt the last block with padding */
+ gnutls_memset(last_block, block_size - r, sizeof(last_block));
+ if (r > 0) {
+ memcpy(last_block, &p[ptext_len - r], r);
+ }
+ ret = _gnutls_cipher_encrypt2(&h->ctx_enc,
+ last_block, block_size,
+ &c[ptext_len - r], block_size);
+ if (ret < 0) {
+ goto error;
+ }
+ *ctext_len = n;
+ } else {
+ if (!ctext) {
+ *ctext_len = ptext_len;
+ return 0;
+ }
+
+ ret = _gnutls_cipher_encrypt2(&h->ctx_enc, ptext, ptext_len,
+ ctext, *ctext_len);
+ if (ret < 0) {
+ goto error;
+ }
+ *ctext_len = ptext_len;
+ }
+
+ error:
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_cipher_decrypt3:
+ * @handle: is a #gnutls_cipher_hd_t type
+ * @ctext: the data to decrypt
+ * @ctext_len: the length of data to decrypt
+ * @ptext: the decrypted data
+ * @ptext_len: the available length for decrypted data
+ * @flags: flags for padding
+ *
+ * This function will decrypt the given data using the algorithm
+ * specified by the context. If @flags is specified, padding for the
+ * decrypted data will be removed accordingly and @ptext_len will be
+ * updated.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.7.7
+ **/
+int
+gnutls_cipher_decrypt3(gnutls_cipher_hd_t handle,
+ const void *ctext, size_t ctext_len,
+ void *ptext, size_t *ptext_len,
+ unsigned flags)
+{
+ api_cipher_hd_st *h = handle;
+ int ret;
+
+ ret = gnutls_cipher_decrypt2(handle,
+ ctext, ctext_len,
+ ptext, *ptext_len);
+ if (ret < 0) {
+ return ret;
+ }
+
+ if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK &&
+ (flags & GNUTLS_CIPHER_PADDING_PKCS7)) {
+ uint8_t *p = ptext;
+ uint8_t padding = p[*ptext_len - 1];
+ if (!padding || padding > _gnutls_cipher_get_block_size(h->ctx_enc.e)) {
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+ /* Check that the prior bytes are all PADDING */
+ for (size_t i = *ptext_len - padding; i < *ptext_len; i++) {
+ if (padding != p[*ptext_len - 1]) {
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+ }
+ *ptext_len -= padding;
+ }
+
+ return 0;
+}
+
+/**
+ * gnutls_cipher_deinit:
+ * @handle: is a #gnutls_cipher_hd_t type
+ *
+ * This function will deinitialize all resources occupied by the given
+ * encryption context.
+ *
+ * Since: 2.10.0
+ **/
+void gnutls_cipher_deinit(gnutls_cipher_hd_t handle)
+{
+ api_cipher_hd_st *h = handle;
+
+ _gnutls_cipher_deinit(&h->ctx_enc);
+ if (_gnutls_cipher_type(h->ctx_enc.e) == CIPHER_BLOCK)
+ _gnutls_cipher_deinit(&h->ctx_dec);
+ gnutls_free(handle);
+}
+
+
+/* HMAC */
+
+
+/**
+ * gnutls_hmac_init:
+ * @dig: is a #gnutls_hmac_hd_t type
+ * @algorithm: the HMAC algorithm to use
+ * @key: the key to be used for encryption
+ * @keylen: the length of the key
+ *
+ * This function will initialize an context that can be used to
+ * produce a Message Authentication Code (MAC) of data. This will
+ * effectively use the current crypto backend in use by gnutls or the
+ * cryptographic accelerator in use.
+ *
+ * Note that despite the name of this function, it can be used
+ * for other MAC algorithms than HMAC.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_hmac_init(gnutls_hmac_hd_t * dig,
+ gnutls_mac_algorithm_t algorithm,
+ const void *key, size_t keylen)
+{
+ int ret;
+ bool not_approved = false;
+
+ /* MD5 is only allowed internally for TLS */
+ if (!is_mac_algo_allowed(algorithm)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_mac_algo_approved_in_fips(algorithm)) {
+ not_approved = true;
+ }
+
+ /* Key lengths of less than 112 bits are not approved */
+ if (keylen < 14) {
+ not_approved = true;
+ }
+
+ *dig = gnutls_malloc(sizeof(mac_hd_st));
+ if (*dig == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret = _gnutls_mac_init(((mac_hd_st *) * dig),
+ mac_to_entry(algorithm), key, keylen);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_hmac_set_nonce:
+ * @handle: is a #gnutls_hmac_hd_t type
+ * @nonce: the data to set as nonce
+ * @nonce_len: the length of data
+ *
+ * This function will set the nonce in the MAC algorithm.
+ *
+ * Since: 3.2.0
+ **/
+void
+gnutls_hmac_set_nonce(gnutls_hmac_hd_t handle, const void *nonce,
+ size_t nonce_len)
+{
+ _gnutls_mac_set_nonce((mac_hd_st *) handle, nonce, nonce_len);
+}
+
+/**
+ * gnutls_hmac:
+ * @handle: is a #gnutls_hmac_hd_t type
+ * @ptext: the data to hash
+ * @ptext_len: the length of data to hash
+ *
+ * This function will hash the given data using the algorithm
+ * specified by the context.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int gnutls_hmac(gnutls_hmac_hd_t handle, const void *ptext, size_t ptext_len)
+{
+ int ret;
+
+ ret = _gnutls_mac((mac_hd_st *) handle, ptext, ptext_len);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_hmac_output:
+ * @handle: is a #gnutls_hmac_hd_t type
+ * @digest: is the output value of the MAC
+ *
+ * This function will output the current MAC value
+ * and reset the state of the MAC.
+ *
+ * Since: 2.10.0
+ **/
+void gnutls_hmac_output(gnutls_hmac_hd_t handle, void *digest)
+{
+ _gnutls_mac_output((mac_hd_st *) handle, digest);
+}
+
+/**
+ * gnutls_hmac_deinit:
+ * @handle: is a #gnutls_hmac_hd_t type
+ * @digest: is the output value of the MAC
+ *
+ * This function will deinitialize all resources occupied by
+ * the given hmac context.
+ *
+ * Since: 2.10.0
+ **/
+void gnutls_hmac_deinit(gnutls_hmac_hd_t handle, void *digest)
+{
+ _gnutls_mac_deinit((mac_hd_st *) handle, digest);
+ gnutls_free(handle);
+}
+
+/**
+ * gnutls_hmac_get_len:
+ * @algorithm: the hmac algorithm to use
+ *
+ * This function will return the length of the output data
+ * of the given hmac algorithm.
+ *
+ * Returns: The length or zero on error.
+ *
+ * Since: 2.10.0
+ **/
+unsigned gnutls_hmac_get_len(gnutls_mac_algorithm_t algorithm)
+{
+ return _gnutls_mac_get_algo_len(mac_to_entry(algorithm));
+}
+
+/**
+ * gnutls_hmac_get_key_size:
+ * @algorithm: the mac algorithm to use
+ *
+ * This function will return the size of the key to be used with this
+ * algorithm. On the algorithms which may accept arbitrary key sizes,
+ * the returned size is the MAC key size used in the TLS protocol.
+ *
+ * Returns: The key size or zero on error.
+ *
+ * Since: 3.6.12
+ **/
+unsigned gnutls_hmac_get_key_size(gnutls_mac_algorithm_t algorithm)
+{
+ return _gnutls_mac_get_key_size(mac_to_entry(algorithm));
+}
+
+/**
+ * gnutls_hmac_fast:
+ * @algorithm: the hash algorithm to use
+ * @key: the key to use
+ * @keylen: the length of the key
+ * @ptext: the data to hash
+ * @ptext_len: the length of data to hash
+ * @digest: is the output value of the hash
+ *
+ * This convenience function will hash the given data and return output
+ * on a single call. Note, this call will not work for MAC algorithms
+ * that require nonce (like UMAC or GMAC).
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_hmac_fast(gnutls_mac_algorithm_t algorithm,
+ const void *key, size_t keylen,
+ const void *ptext, size_t ptext_len, void *digest)
+{
+ int ret;
+ bool not_approved = false;
+
+ if (!is_mac_algo_allowed(algorithm)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_mac_algo_approved_in_fips(algorithm)) {
+ not_approved = true;
+ }
+
+ /* Key lengths of less than 112 bits are not approved */
+ if (keylen < 14) {
+ not_approved = true;
+ }
+
+ ret = _gnutls_mac_fast(algorithm, key, keylen, ptext, ptext_len,
+ digest);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_hmac_copy:
+ * @handle: is a #gnutls_hmac_hd_t type
+ *
+ * This function will create a copy of MAC context, containing all its current
+ * state. Copying contexts for MACs registered using
+ * gnutls_crypto_register_mac() is not supported and will always result in an
+ * error. In addition to that, some of the MAC implementations do not support
+ * this operation. Applications should check the return value and provide a
+ * proper fallback.
+ *
+ * Returns: new MAC context or NULL in case of an error.
+ *
+ * Since: 3.6.9
+ */
+gnutls_hmac_hd_t gnutls_hmac_copy(gnutls_hmac_hd_t handle)
+{
+ gnutls_hmac_hd_t dig;
+
+ dig = gnutls_malloc(sizeof(mac_hd_st));
+ if (dig == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return NULL;
+ }
+
+ if (_gnutls_mac_copy((const mac_hd_st *) handle, (mac_hd_st *)dig) != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(dig);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return NULL;
+ }
+
+ return dig;
+}
+
+/* HASH */
+
+/**
+ * gnutls_hash_init:
+ * @dig: is a #gnutls_hash_hd_t type
+ * @algorithm: the hash algorithm to use
+ *
+ * This function will initialize an context that can be used to
+ * produce a Message Digest of data. This will effectively use the
+ * current crypto backend in use by gnutls or the cryptographic
+ * accelerator in use.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_hash_init(gnutls_hash_hd_t * dig,
+ gnutls_digest_algorithm_t algorithm)
+{
+ int ret;
+ bool not_approved = false;
+
+ if (!is_mac_algo_allowed(DIG_TO_MAC(algorithm))) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_mac_algo_approved_in_fips(DIG_TO_MAC(algorithm))) {
+ not_approved = true;
+ }
+
+ *dig = gnutls_malloc(sizeof(digest_hd_st));
+ if (*dig == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret = _gnutls_hash_init(((digest_hd_st *) * dig),
+ hash_to_entry(algorithm));
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_hash:
+ * @handle: is a #gnutls_hash_hd_t type
+ * @ptext: the data to hash
+ * @ptext_len: the length of data to hash
+ *
+ * This function will hash the given data using the algorithm
+ * specified by the context.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int gnutls_hash(gnutls_hash_hd_t handle, const void *ptext, size_t ptext_len)
+{
+ int ret;
+
+ ret = _gnutls_hash((digest_hd_st *) handle, ptext, ptext_len);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+ return ret;
+}
+
+/**
+ * gnutls_hash_output:
+ * @handle: is a #gnutls_hash_hd_t type
+ * @digest: is the output value of the hash
+ *
+ * This function will output the current hash value
+ * and reset the state of the hash.
+ *
+ * Since: 2.10.0
+ **/
+void gnutls_hash_output(gnutls_hash_hd_t handle, void *digest)
+{
+ _gnutls_hash_output((digest_hd_st *) handle, digest);
+}
+
+/**
+ * gnutls_hash_deinit:
+ * @handle: is a #gnutls_hash_hd_t type
+ * @digest: is the output value of the hash
+ *
+ * This function will deinitialize all resources occupied by
+ * the given hash context.
+ *
+ * Since: 2.10.0
+ **/
+void gnutls_hash_deinit(gnutls_hash_hd_t handle, void *digest)
+{
+ _gnutls_hash_deinit((digest_hd_st *) handle, digest);
+ gnutls_free(handle);
+}
+
+/**
+ * gnutls_hash_get_len:
+ * @algorithm: the hash algorithm to use
+ *
+ * This function will return the length of the output data
+ * of the given hash algorithm.
+ *
+ * Returns: The length or zero on error.
+ *
+ * Since: 2.10.0
+ **/
+unsigned gnutls_hash_get_len(gnutls_digest_algorithm_t algorithm)
+{
+ return _gnutls_hash_get_algo_len(hash_to_entry(algorithm));
+}
+
+/**
+ * gnutls_hash_fast:
+ * @algorithm: the hash algorithm to use
+ * @ptext: the data to hash
+ * @ptext_len: the length of data to hash
+ * @digest: is the output value of the hash
+ *
+ * This convenience function will hash the given data and return output
+ * on a single call.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 2.10.0
+ **/
+int
+gnutls_hash_fast(gnutls_digest_algorithm_t algorithm,
+ const void *ptext, size_t ptext_len, void *digest)
+{
+ int ret;
+ bool not_approved = false;
+
+ if (!is_mac_algo_allowed(DIG_TO_MAC(algorithm))) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_mac_algo_approved_in_fips(DIG_TO_MAC(algorithm))) {
+ not_approved = true;
+ }
+
+ ret = _gnutls_hash_fast(algorithm, ptext, ptext_len, digest);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_hash_copy:
+ * @handle: is a #gnutls_hash_hd_t type
+ *
+ * This function will create a copy of Message Digest context, containing all
+ * its current state. Copying contexts for Message Digests registered using
+ * gnutls_crypto_register_digest() is not supported and will always result in
+ * an error. In addition to that, some of the Message Digest implementations do
+ * not support this operation. Applications should check the return value and
+ * provide a proper fallback.
+ *
+ * Returns: new Message Digest context or NULL in case of an error.
+ *
+ * Since: 3.6.9
+ */
+gnutls_hash_hd_t gnutls_hash_copy(gnutls_hash_hd_t handle)
+{
+ gnutls_hash_hd_t dig;
+
+ dig = gnutls_malloc(sizeof(digest_hd_st));
+ if (dig == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return NULL;
+ }
+
+ if (_gnutls_hash_copy((const digest_hd_st *) handle, (digest_hd_st *)dig) != GNUTLS_E_SUCCESS) {
+ gnutls_assert();
+ gnutls_free(dig);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return NULL;
+ }
+
+ return dig;
+}
+
+/**
+ * gnutls_key_generate:
+ * @key: is a pointer to a #gnutls_datum_t which will contain a newly
+ * created key
+ * @key_size: the number of bytes of the key
+ *
+ * Generates a random key of @key_size bytes.
+ *
+ * Returns: On success, %GNUTLS_E_SUCCESS (0) is returned, or an
+ * error code.
+ *
+ * Since: 3.0
+ **/
+int gnutls_key_generate(gnutls_datum_t * key, unsigned int key_size)
+{
+ int ret;
+
+ FAIL_IF_LIB_ERROR;
+
+#ifdef ENABLE_FIPS140
+ /* The FIPS140 approved RNGs are not allowed to be used
+ * to extract key sizes longer than their original seed.
+ */
+ if (_gnutls_fips_mode_enabled() != 0 &&
+ key_size > FIPS140_RND_KEY_SIZE)
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+#endif
+
+ key->size = key_size;
+ key->data = gnutls_malloc(key->size);
+ if (!key->data) {
+ gnutls_assert();
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret = gnutls_rnd(GNUTLS_RND_RANDOM, key->data, key->size);
+ if (ret < 0) {
+ gnutls_assert();
+ _gnutls_free_datum(key);
+ return ret;
+ }
+
+ return 0;
+}
+
+/* AEAD API */
+
+/**
+ * gnutls_aead_cipher_init:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @cipher: the authenticated-encryption algorithm to use
+ * @key: The key to be used for encryption
+ *
+ * This function will initialize an context that can be used for
+ * encryption/decryption of data. This will effectively use the
+ * current crypto backend in use by gnutls or the cryptographic
+ * accelerator in use.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.4.0
+ **/
+int gnutls_aead_cipher_init(gnutls_aead_cipher_hd_t *handle,
+ gnutls_cipher_algorithm_t cipher,
+ const gnutls_datum_t *key)
+{
+ api_aead_cipher_hd_st *h;
+ const cipher_entry_st *e;
+ int ret;
+ bool not_approved = false;
+
+ if (!is_cipher_algo_allowed(cipher)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_cipher_algo_approved_in_fips(cipher)) {
+ not_approved = true;
+ }
+
+ e = cipher_to_entry(cipher);
+ if (e == NULL || e->type != CIPHER_AEAD) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ h = gnutls_calloc(1, sizeof(api_aead_cipher_hd_st));
+ if (h == NULL) {
+ gnutls_assert();
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return GNUTLS_E_MEMORY_ERROR;
+ }
+
+ ret = _gnutls_aead_cipher_init(h, cipher, key);
+ if (ret < 0) {
+ gnutls_free(h);
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return ret;
+ }
+
+ *handle = h;
+
+ if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_aead_cipher_set_key:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @key: The key to be used for encryption
+ *
+ * This function will set a new key without re-initializing the
+ * context.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.7.5
+ **/
+int gnutls_aead_cipher_set_key(gnutls_aead_cipher_hd_t handle,
+ const gnutls_datum_t *key)
+{
+ const cipher_entry_st* e;
+ int ret;
+
+ e = cipher_to_entry(handle->ctx_enc.e->id);
+ if (e == NULL || e->type != CIPHER_AEAD) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ ret = handle->ctx_enc.setkey(handle->ctx_enc.handle,
+ key->data, key->size);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ }
+
+ return ret;
+}
+
+/**
+ * gnutls_aead_cipher_decrypt:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @nonce: the nonce to set
+ * @nonce_len: The length of the nonce
+ * @auth: additional data to be authenticated
+ * @auth_len: The length of the data
+ * @tag_size: The size of the tag to use (use zero for the default)
+ * @ctext: the data to decrypt (including the authentication tag)
+ * @ctext_len: the length of data to decrypt (includes tag size)
+ * @ptext: the decrypted data
+ * @ptext_len: the length of decrypted data (initially must hold the maximum available size)
+ *
+ * This function will decrypt the given data using the algorithm
+ * specified by the context. This function must be provided the complete
+ * data to be decrypted, including the authentication tag. On several
+ * AEAD ciphers, the authentication tag is appended to the ciphertext,
+ * though this is not a general rule. This function will fail if
+ * the tag verification fails.
+ *
+ * Returns: Zero or a negative error code on verification failure or other error.
+ *
+ * Since: 3.4.0
+ **/
+int
+gnutls_aead_cipher_decrypt(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const void *auth, size_t auth_len,
+ size_t tag_size,
+ const void *ctext, size_t ctext_len,
+ void *ptext, size_t *ptext_len)
+{
+ int ret;
+ api_aead_cipher_hd_st *h = handle;
+
+ 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)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if (unlikely(ctext_len < tag_size)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+
+ ret = _gnutls_aead_cipher_decrypt(&h->ctx_enc,
+ nonce, nonce_len,
+ auth, auth_len,
+ tag_size,
+ ctext, ctext_len,
+ ptext, *ptext_len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ /* That assumes that AEAD ciphers are stream */
+ *ptext_len = ctext_len - tag_size;
+
+ return 0;
+}
+
+
+/**
+ * gnutls_aead_cipher_encrypt:
+ * @handle: is a #gnutls_aead_cipher_hd_t type.
+ * @nonce: the nonce to set
+ * @nonce_len: The length of the nonce
+ * @auth: additional data to be authenticated
+ * @auth_len: The length of the data
+ * @tag_size: The size of the tag to use (use zero for the default)
+ * @ptext: the data to encrypt
+ * @ptext_len: The length of data to encrypt
+ * @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 given data 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.4.0
+ **/
+int
+gnutls_aead_cipher_encrypt(gnutls_aead_cipher_hd_t handle,
+ const void *nonce, size_t nonce_len,
+ const void *auth, size_t auth_len,
+ size_t tag_size,
+ const void *ptext, size_t ptext_len,
+ void *ctext, size_t *ctext_len)
+{
+ api_aead_cipher_hd_st *h = handle;
+ 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)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ if (unlikely(*ctext_len < ptext_len + tag_size)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ }
+
+ ret = _gnutls_aead_cipher_encrypt(&h->ctx_enc,
+ nonce, nonce_len,
+ auth, auth_len,
+ tag_size,
+ ptext, ptext_len,
+ ctext, *ctext_len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+
+ /* That assumes that AEAD ciphers are stream */
+ *ctext_len = ptext_len + tag_size;
+
+ return 0;
+}
+
+struct iov_store_st {
+ void *data;
+ size_t length;
+ size_t capacity;
+};
+
+static void iov_store_free(struct iov_store_st *s)
+{
+ gnutls_free(s->data);
+}
+
+static int iov_store_grow(struct iov_store_st *s, size_t length)
+{
+ void *new_data;
+ size_t new_capacity = s->capacity;
+
+ if (INT_ADD_OVERFLOW(new_capacity, length)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ new_capacity += length;
+ new_data = gnutls_realloc(s->data, new_capacity);
+ if (!new_data) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ s->data = new_data;
+ s->capacity = new_capacity;
+ return 0;
+}
+
+static int
+append_from_iov(struct iov_store_st *dst, const giovec_t *iov, int iovcnt)
+{
+ if (iovcnt > 0) {
+ int i;
+ uint8_t *p;
+ void *new_data;
+ size_t new_capacity = dst->capacity;
+
+ for (i = 0; i < iovcnt; i++) {
+ if (INT_ADD_OVERFLOW(new_capacity, iov[i].iov_len)) {
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+ new_capacity += iov[i].iov_len;
+ }
+ new_data = gnutls_realloc(dst->data, new_capacity);
+ if (!new_data) {
+ return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR);
+ }
+ dst->data = new_data;
+ dst->capacity = new_capacity;
+
+ p = (uint8_t *) dst->data + dst->length;
+ for (i = 0; i < iovcnt; i++) {
+ if (iov[i].iov_len > 0) {
+ memcpy(p, iov[i].iov_base, iov[i].iov_len);
+ }
+ p += iov[i].iov_len;
+ dst->length += iov[i].iov_len;
+ }
+ }
+ return 0;
+}
+
+static int
+copy_to_iov(const uint8_t *data, size_t size,
+ const giovec_t *iov, int iovcnt)
+{
+ size_t offset = 0;
+ int i;
+
+ 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 *) data + offset, to_copy);
+ offset += to_copy;
+ size -= to_copy;
+ }
+ if (size > 0)
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ return 0;
+}
+
+#define IOV_STORE_INIT { NULL, 0, 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 = IOV_STORE_INIT;
+ struct iov_store_st ptext = IOV_STORE_INIT;
+ int ret;
+
+ 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 = append_from_iov(&auth, auth_iov, auth_iovcnt);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = append_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.length,
+ tag_size,
+ ptext.data, ptext.length,
+ 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)
+{
+ int ret;
+ uint8_t *dst;
+ size_t dst_size, total = 0;
+ uint8_t *p;
+ size_t len;
+ size_t blocksize = handle->ctx_enc.e->blocksize;
+ struct iov_iter_st iter;
+
+ 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);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+ ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ dst = ctext;
+ dst_size = *ctext_len;
+
+ ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+ len = ret;
+ ret = _gnutls_cipher_encrypt2(&handle->ctx_enc,
+ p, len,
+ dst, dst_size);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ DECR_LEN(dst_size, len);
+ dst += len;
+ total += len;
+ }
+
+ if (dst_size < tag_size) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
+ }
+
+ _gnutls_cipher_tag(&handle->ctx_enc, dst, tag_size);
+
+ total += tag_size;
+ *ctext_len = total;
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ return 0;
+}
+
+/**
+ * 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)
+{
+ /* 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)
+{
+ struct iov_store_st auth = IOV_STORE_INIT;
+ struct iov_store_st ptext = IOV_STORE_INIT;
+ uint8_t *ptext_data;
+ size_t ptext_size;
+ uint8_t *ctext_data;
+ size_t ctext_size;
+ uint8_t *_tag;
+ size_t _tag_size;
+ int ret;
+
+ if (tag_size == NULL || *tag_size == 0)
+ _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(handle->ctx_enc.e)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+ }
+
+ ret = append_from_iov(&auth, auth_iov, auth_iovcnt);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ if (handle->ctx_enc.e->flags & GNUTLS_CIPHER_FLAG_TAG_PREFIXED) {
+ /* prepend space for tag */
+ ret = iov_store_grow(&ptext, _tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ ptext.length = _tag_size;
+
+ ret = append_from_iov(&ptext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* We must set ptext_data after the above
+ * grow/append opereations, otherwise it will point to an invalid pointer after realloc.
+ */
+ ptext_data = (uint8_t *) ptext.data + _tag_size;
+ ptext_size = ptext.length - _tag_size;
+ } else {
+ ret = append_from_iov(&ptext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* append space for tag */
+ ret = iov_store_grow(&ptext, _tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* We must set ptext_data after the above
+ * grow/append opereations, otherwise it will point to an invalid pointer after realloc.
+ */
+ ptext_data = ptext.data;
+ ptext_size = ptext.length;
+ }
+
+ ctext_size = ptext.capacity;
+ ret = gnutls_aead_cipher_encrypt(handle, nonce, nonce_len,
+ auth.data, auth.length,
+ _tag_size,
+ ptext_data, ptext_size,
+ ptext.data, &ctext_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (handle->ctx_enc.e->flags & GNUTLS_CIPHER_FLAG_TAG_PREFIXED) {
+ ctext_data = (uint8_t *)ptext.data + _tag_size;
+ _tag = ptext.data;
+ } else {
+ ctext_data = ptext.data;
+ _tag = (uint8_t *)ptext.data + ptext_size;
+ }
+
+ ret = copy_to_iov(ctext_data, ptext_size, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ if (tag != NULL) {
+ memcpy(tag, _tag, _tag_size);
+ }
+ if (tag_size != NULL) {
+ *tag_size = _tag_size;
+ }
+
+ error:
+ 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;
+}
+
+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);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+ ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ 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)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+
+ len = ret;
+ ret = _gnutls_cipher_encrypt2(&handle->ctx_enc, p, len, p, len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_sync(&iter, p, len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ 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;
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ return 0;
+}
+
+/**
+ * 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)
+{
+ /* 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 = IOV_STORE_INIT;
+ struct iov_store_st ctext = IOV_STORE_INIT;
+ uint8_t *ctext_data;
+ size_t ptext_size;
+ int ret;
+
+ 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 = append_from_iov(&auth, auth_iov, auth_iovcnt);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ if (handle->ctx_enc.e->flags & GNUTLS_CIPHER_FLAG_TAG_PREFIXED) {
+ /* prepend tag */
+ ret = iov_store_grow(&ctext, tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ memcpy(ctext.data, tag, tag_size);
+ ctext.length += tag_size;
+
+ ret = append_from_iov(&ctext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* We must set ctext_data after the above
+ * grow/append opereations, otherwise it will point to an invalid pointer after realloc.
+ */
+ ctext_data = (uint8_t *) ctext.data + tag_size;
+ } else {
+ ret = append_from_iov(&ctext, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ /* append tag */
+ ret = iov_store_grow(&ctext, tag_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+ memcpy((uint8_t *) ctext.data + ctext.length, tag, tag_size);
+ ctext.length += tag_size;
+
+ /* We must set ctext_data after the above
+ * grow/append opereations, otherwise it will point to an invalid pointer after realloc.
+ */
+ ctext_data = ctext.data;
+ }
+
+ ptext_size = ctext.capacity;
+ ret = gnutls_aead_cipher_decrypt(handle, nonce, nonce_len,
+ auth.data, auth.length,
+ tag_size,
+ ctext.data, ctext.length,
+ ctext_data, &ptext_size);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ ret = copy_to_iov(ctext.data, ptext_size, iov, iovcnt);
+ if (ret < 0) {
+ gnutls_assert();
+ goto error;
+ }
+
+ error:
+ 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;
+}
+
+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);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, auth_iov, auth_iovcnt, blocksize);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+ ret = _gnutls_cipher_auth(&handle->ctx_enc, p, ret);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ }
+
+ ret = _gnutls_iov_iter_init(&iter, iov, iovcnt, blocksize);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ while (1) {
+ ret = _gnutls_iov_iter_next(&iter, &p);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+ if (ret == 0)
+ break;
+
+ len = ret;
+ ret = _gnutls_cipher_decrypt2(&handle->ctx_enc, p, len, p, len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(ret);
+ }
+
+ ret = _gnutls_iov_iter_sync(&iter, p, len);
+ if (unlikely(ret < 0)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ 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) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_DECRYPTION_FAILED);
+ }
+ }
+
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ 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.
+ *
+ * This function will deinitialize all resources occupied by the given
+ * authenticated-encryption context.
+ *
+ * Since: 3.4.0
+ **/
+void gnutls_aead_cipher_deinit(gnutls_aead_cipher_hd_t handle)
+{
+ _gnutls_aead_cipher_deinit(handle);
+ gnutls_free(handle);
+}
+
+extern gnutls_crypto_kdf_st _gnutls_kdf_ops;
+
+/* Same as @gnutls_hkdf_extract but without changing FIPS context */
+int
+_gnutls_hkdf_extract(gnutls_mac_algorithm_t mac,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *salt,
+ void *output)
+{
+ /* MD5 is only allowed internally for TLS */
+ if (!is_mac_algo_allowed(mac)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ }
+
+ /* We don't check whether MAC is approved, because HKDF is
+ * only approved in TLS, which is handled separately. */
+
+ return _gnutls_kdf_ops.hkdf_extract(mac, key->data, key->size,
+ salt ? salt->data : NULL,
+ salt ? salt->size : 0,
+ output);
+}
+
+/**
+ * gnutls_hkdf_extract:
+ * @mac: the mac algorithm used internally
+ * @key: the initial keying material
+ * @salt: the optional salt
+ * @output: the output value of the extract operation
+ *
+ * This function will derive a fixed-size key using the HKDF-Extract
+ * function as defined in RFC 5869.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.13
+ */
+int
+gnutls_hkdf_extract(gnutls_mac_algorithm_t mac,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *salt,
+ void *output)
+{
+ int ret;
+
+ ret = _gnutls_hkdf_extract(mac, key, salt, output);
+ if (ret < 0)
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ else
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+
+ return ret;
+}
+
+/* Same as @gnutls_hkdf_expand but without changing FIPS context */
+int
+_gnutls_hkdf_expand(gnutls_mac_algorithm_t mac,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *info,
+ void *output, size_t length)
+{
+ /* MD5 is only allowed internally for TLS */
+ if (!is_mac_algo_allowed(mac)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ }
+
+ /* We don't check whether MAC is approved, because HKDF is
+ * only approved in TLS, which is handled separately. */
+
+ return _gnutls_kdf_ops.hkdf_expand(mac, key->data, key->size,
+ info->data, info->size,
+ output, length);
+}
+
+/**
+ * gnutls_hkdf_expand:
+ * @mac: the mac algorithm used internally
+ * @key: the pseudorandom key created with HKDF-Extract
+ * @info: the optional informational data
+ * @output: the output value of the expand operation
+ * @length: the desired length of the output key
+ *
+ * This function will derive a variable length keying material from
+ * the pseudorandom key using the HKDF-Expand function as defined in
+ * RFC 5869.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.13
+ */
+int
+gnutls_hkdf_expand(gnutls_mac_algorithm_t mac,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *info,
+ void *output, size_t length)
+{
+ int ret;
+
+ ret = _gnutls_hkdf_expand(mac, key, info, output, length);
+ if (ret < 0)
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ else
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+
+ return ret;
+}
+
+/**
+ * gnutls_pbkdf2:
+ * @mac: the mac algorithm used internally
+ * @key: the initial keying material
+ * @salt: the salt
+ * @iter_count: the iteration count
+ * @output: the output value
+ * @length: the desired length of the output key
+ *
+ * This function will derive a variable length keying material from
+ * a password according to PKCS #5 PBKDF2.
+ *
+ * Returns: Zero or a negative error code on error.
+ *
+ * Since: 3.6.13
+ */
+int
+gnutls_pbkdf2(gnutls_mac_algorithm_t mac,
+ const gnutls_datum_t *key,
+ const gnutls_datum_t *salt,
+ unsigned iter_count,
+ void *output, size_t length)
+{
+ int ret;
+ bool not_approved = false;
+
+ /* MD5 is only allowed internally for TLS */
+ if (!is_mac_algo_allowed(mac)) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ return gnutls_assert_val(GNUTLS_E_UNWANTED_ALGORITHM);
+ } else if (!is_mac_algo_approved_in_fips(mac)) {
+ not_approved = true;
+ }
+
+ ret = _gnutls_kdf_ops.pbkdf2(mac, key->data, key->size,
+ salt->data, salt->size, iter_count,
+ output, length);
+ if (ret < 0) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_ERROR);
+ } else if (not_approved) {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_NOT_APPROVED);
+ } else {
+ _gnutls_switch_fips_state(GNUTLS_FIPS140_OP_APPROVED);
+ }
+ return ret;
+}