/*
* Copyright (C) 2009-2012 Free Software Foundation, 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 3 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
*
*/
#include
#include
#include
#include
#include
#include
#include
#define SR(x, cleanup) if ( (x)<0 ) { \
gnutls_assert(); \
ret = GNUTLS_E_INTERNAL_ERROR; \
goto cleanup; \
}
/* Returns true(non-zero) or false(0) if the
* provided cipher exists
*/
int _gnutls_cipher_exists(gnutls_cipher_algorithm_t cipher)
{
const gnutls_crypto_cipher_st *cc;
int ret;
cc = _gnutls_get_crypto_cipher (cipher);
if (cc != NULL) return 1;
ret = _gnutls_cipher_ops.exists(cipher);
return ret;
}
int
_gnutls_cipher_init (cipher_hd_st * handle, gnutls_cipher_algorithm_t cipher,
const gnutls_datum_t * key, const gnutls_datum_t * iv, int enc)
{
int ret = GNUTLS_E_INTERNAL_ERROR;
const gnutls_crypto_cipher_st *cc = NULL;
if (cipher == GNUTLS_CIPHER_NULL)
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
handle->is_aead = _gnutls_cipher_algo_is_aead(cipher);
if (handle->is_aead)
handle->tag_size = gnutls_cipher_get_block_size(cipher);
/* check if a cipher has been registered
*/
cc = _gnutls_get_crypto_cipher (cipher);
if (cc != NULL)
{
handle->encrypt = cc->encrypt;
handle->decrypt = cc->decrypt;
handle->deinit = cc->deinit;
handle->auth = cc->auth;
handle->tag = cc->tag;
handle->setiv = cc->setiv;
SR (cc->init (cipher, &handle->handle, enc), cc_cleanup);
SR (cc->setkey( handle->handle, key->data, key->size), cc_cleanup);
if (iv)
{
SR (cc->setiv( handle->handle, iv->data, iv->size), cc_cleanup);
}
return 0;
}
handle->encrypt = _gnutls_cipher_ops.encrypt;
handle->decrypt = _gnutls_cipher_ops.decrypt;
handle->deinit = _gnutls_cipher_ops.deinit;
handle->auth = _gnutls_cipher_ops.auth;
handle->tag = _gnutls_cipher_ops.tag;
handle->setiv = _gnutls_cipher_ops.setiv;
/* otherwise use generic cipher interface
*/
ret = _gnutls_cipher_ops.init (cipher, &handle->handle, enc);
if (ret < 0)
{
gnutls_assert ();
return ret;
}
ret = _gnutls_cipher_ops.setkey(handle->handle, key->data, key->size);
if (ret < 0)
{
gnutls_assert ();
goto cc_cleanup;
}
if (iv)
{
ret = _gnutls_cipher_ops.setiv(handle->handle, iv->data, iv->size);
if (ret < 0)
{
gnutls_assert ();
goto cc_cleanup;
}
}
return 0;
cc_cleanup:
if (handle->handle)
handle->deinit (handle->handle);
return ret;
}
/* Auth_cipher API
*/
int _gnutls_auth_cipher_init (auth_cipher_hd_st * handle,
gnutls_cipher_algorithm_t cipher,
const gnutls_datum_t * cipher_key,
const gnutls_datum_t * iv,
gnutls_mac_algorithm_t mac,
const gnutls_datum_t * mac_key,
int ssl_hmac, int enc)
{
int ret;
memset(handle, 0, sizeof(*handle));
if (cipher != GNUTLS_CIPHER_NULL)
{
ret = _gnutls_cipher_init(&handle->cipher, cipher, cipher_key, iv, enc);
if (ret < 0)
return gnutls_assert_val(ret);
}
else
handle->is_null = 1;
if (mac != GNUTLS_MAC_AEAD)
{
handle->is_mac = 1;
handle->ssl_hmac = ssl_hmac;
if (ssl_hmac)
ret = _gnutls_mac_init_ssl3(&handle->mac, mac, mac_key->data, mac_key->size);
else
ret = _gnutls_hmac_init(&handle->mac, mac, mac_key->data, mac_key->size);
if (ret < 0)
{
gnutls_assert();
goto cleanup;
}
handle->tag_size = _gnutls_hash_get_algo_len(mac);
}
else if (_gnutls_cipher_is_aead(&handle->cipher))
handle->tag_size = _gnutls_cipher_tag_len(&handle->cipher);
return 0;
cleanup:
if (handle->is_null == 0)
_gnutls_cipher_deinit(&handle->cipher);
return ret;
}
int _gnutls_auth_cipher_add_auth (auth_cipher_hd_st * handle, const void *text,
int textlen)
{
if (handle->is_mac)
{
if (handle->ssl_hmac)
return _gnutls_hash(&handle->mac, text, textlen);
else
return _gnutls_hmac(&handle->mac, text, textlen);
}
else if (_gnutls_cipher_is_aead(&handle->cipher))
return _gnutls_cipher_auth(&handle->cipher, text, textlen);
else
return 0;
}
int _gnutls_auth_cipher_encrypt2_tag (auth_cipher_hd_st * handle, const uint8_t *text,
int textlen, void *ciphertext, int ciphertextlen,
void* tag_ptr, int tag_size,
int auth_size)
{
int ret;
if (handle->is_mac)
{
if (handle->ssl_hmac)
ret = _gnutls_hash(&handle->mac, text, auth_size);
else
ret = _gnutls_hmac(&handle->mac, text, auth_size);
if (ret < 0)
{
gnutls_assert();
return ret;
}
ret = _gnutls_auth_cipher_tag(handle, tag_ptr, tag_size);
if (ret < 0)
return gnutls_assert_val(ret);
if (handle->is_null==0)
{
ret = _gnutls_cipher_encrypt2(&handle->cipher, text, textlen, ciphertext, ciphertextlen);
if (ret < 0)
return gnutls_assert_val(ret);
}
}
else if (_gnutls_cipher_is_aead(&handle->cipher))
{
ret = _gnutls_cipher_encrypt2(&handle->cipher, text, textlen, ciphertext, ciphertextlen);
if (ret < 0)
return gnutls_assert_val(ret);
ret = _gnutls_auth_cipher_tag(handle, tag_ptr, tag_size);
if (ret < 0)
return gnutls_assert_val(ret);
}
return 0;
}
int _gnutls_auth_cipher_decrypt2 (auth_cipher_hd_st * handle,
const void *ciphertext, int ciphertextlen,
void *text, int textlen)
{
int ret;
if (handle->is_null==0)
{
ret = _gnutls_cipher_decrypt2(&handle->cipher, ciphertext, ciphertextlen,
text, textlen);
if (ret < 0)
return gnutls_assert_val(ret);
}
if (handle->is_mac)
{
/* The MAC is not to be hashed */
textlen -= handle->tag_size;
if (handle->ssl_hmac)
return _gnutls_hash(&handle->mac, text, textlen);
else
return _gnutls_hmac(&handle->mac, text, textlen);
}
return 0;
}
int _gnutls_auth_cipher_tag(auth_cipher_hd_st * handle, void* tag, int tag_size)
{
int ret = 0;
if (handle->is_mac)
{
if (handle->ssl_hmac)
{
ret = _gnutls_mac_output_ssl3 (&handle->mac, tag);
if (ret < 0)
return gnutls_assert_val(ret);
_gnutls_mac_reset_ssl3 (&handle->mac);
}
else
{
_gnutls_hmac_output (&handle->mac, tag);
_gnutls_hmac_reset (&handle->mac);
}
}
else if (_gnutls_cipher_is_aead(&handle->cipher))
{
_gnutls_cipher_tag(&handle->cipher, tag, tag_size);
}
return 0;
}
void _gnutls_auth_cipher_deinit (auth_cipher_hd_st * handle)
{
if (handle->is_mac)
{
if (handle->ssl_hmac) /* failure here doesn't matter */
_gnutls_mac_deinit_ssl3 (&handle->mac, NULL);
else
_gnutls_hmac_deinit(&handle->mac, NULL);
}
if (handle->is_null==0)
_gnutls_cipher_deinit(&handle->cipher);
}