Blame lib/cipher_int.c

Packit 549fdc
/*
Packit 549fdc
 * Copyright (C) 2009-2013 Free Software Foundation, Inc.
Packit 549fdc
 * Copyright (C) 2013 Nikos Mavrogiannopoulos
Packit 549fdc
 *
Packit 549fdc
 * Author: Nikos Mavrogiannopoulos
Packit 549fdc
 *
Packit 549fdc
 * This file is part of GnuTLS.
Packit 549fdc
 *
Packit 549fdc
 * The GnuTLS is free software; you can redistribute it and/or
Packit 549fdc
 * modify it under the terms of the GNU Lesser General Public License
Packit 549fdc
 * as published by the Free Software Foundation; either version 2.1 of
Packit 549fdc
 * the License, or (at your option) any later version.
Packit 549fdc
 *
Packit 549fdc
 * This library is distributed in the hope that it will be useful, but
Packit 549fdc
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
 * Lesser General Public License for more details.
Packit 549fdc
 *
Packit 549fdc
 * You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
Packit 549fdc
 *
Packit 549fdc
 */
Packit 549fdc
Packit 549fdc
#include "gnutls_int.h"
Packit 549fdc
#include "errors.h"
Packit 549fdc
#include <cipher_int.h>
Packit 549fdc
#include <datum.h>
Packit 549fdc
#include <gnutls/crypto.h>
Packit 549fdc
#include <crypto.h>
Packit 549fdc
#include <fips.h>
Packit 549fdc
#include <algorithms.h>
Packit 549fdc
Packit 549fdc
#define SR_FB(x, cleanup) ret=(x); if ( ret<0 ) { \
Packit 549fdc
  if (ret == GNUTLS_E_NEED_FALLBACK) { \
Packit 549fdc
    if (handle->handle) \
Packit 549fdc
	handle->deinit(handle->handle); \
Packit 549fdc
    goto fallback; \
Packit 549fdc
  } \
Packit 549fdc
  gnutls_assert(); \
Packit 549fdc
  ret = GNUTLS_E_INTERNAL_ERROR; \
Packit 549fdc
  goto cleanup; \
Packit 549fdc
  }
Packit 549fdc
Packit 549fdc
#define SR(x, cleanup) if ( (x)<0 ) { \
Packit 549fdc
  gnutls_assert(); \
Packit 549fdc
  ret = GNUTLS_E_INTERNAL_ERROR; \
Packit 549fdc
  goto cleanup; \
Packit 549fdc
  }
Packit 549fdc
Packit 549fdc
/* Returns true(non-zero) or false(0) if the 
Packit 549fdc
 * provided cipher exists
Packit 549fdc
 */
Packit 549fdc
int _gnutls_cipher_exists(gnutls_cipher_algorithm_t cipher)
Packit 549fdc
{
Packit 549fdc
	const gnutls_crypto_cipher_st *cc;
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	/* All the other ciphers are disabled on the back-end library.
Packit 549fdc
	 * The NULL needs to be detected here as it is not a cipher
Packit 549fdc
	 * that is provided by the back-end.
Packit 549fdc
	 */
Packit 549fdc
	if (cipher == GNUTLS_CIPHER_NULL) {
Packit 549fdc
		if (_gnutls_fips_mode_enabled() == 0)
Packit 549fdc
			return 1;
Packit 549fdc
		else
Packit 549fdc
			return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	cc = _gnutls_get_crypto_cipher(cipher);
Packit 549fdc
	if (cc != NULL)
Packit 549fdc
		return 1;
Packit 549fdc
Packit 549fdc
	ret = _gnutls_cipher_ops.exists(cipher);
Packit 549fdc
	return ret;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
int
Packit 549fdc
_gnutls_cipher_init(cipher_hd_st *handle, const cipher_entry_st *e,
Packit 549fdc
		    const gnutls_datum_t *key, const gnutls_datum_t *iv,
Packit 549fdc
		    int enc)
Packit 549fdc
{
Packit 549fdc
	int ret = GNUTLS_E_INTERNAL_ERROR;
Packit 549fdc
	const gnutls_crypto_cipher_st *cc = NULL;
Packit 549fdc
Packit 549fdc
	if (unlikely(e == NULL || e->id == GNUTLS_CIPHER_NULL))
Packit 549fdc
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit 549fdc
Packit 549fdc
	FAIL_IF_LIB_ERROR;
Packit 549fdc
Packit 549fdc
	handle->e = e;
Packit 549fdc
	handle->handle = NULL;
Packit 549fdc
Packit 549fdc
	/* check if a cipher has been registered
Packit 549fdc
	 */
Packit 549fdc
	cc = _gnutls_get_crypto_cipher(e->id);
Packit 549fdc
	if (cc != NULL) {
Packit 549fdc
		handle->encrypt = cc->encrypt;
Packit 549fdc
		handle->decrypt = cc->decrypt;
Packit 549fdc
		handle->aead_encrypt = cc->aead_encrypt;
Packit 549fdc
		handle->aead_decrypt = cc->aead_decrypt;
Packit 549fdc
		handle->deinit = cc->deinit;
Packit 549fdc
		handle->auth = cc->auth;
Packit 549fdc
		handle->tag = cc->tag;
Packit 549fdc
		handle->setiv = cc->setiv;
Packit 549fdc
Packit 549fdc
		/* if cc->init() returns GNUTLS_E_NEED_FALLBACK we
Packit 549fdc
		 * use the default ciphers */
Packit 549fdc
		SR_FB(cc->init(e->id, &handle->handle, enc), cc_cleanup);
Packit 549fdc
		SR_FB(cc->setkey(handle->handle, key->data, key->size),
Packit 549fdc
		   cc_cleanup);
Packit 549fdc
		if (iv) {
Packit 549fdc
			if (unlikely(cc->setiv == NULL)) /* the API doesn't accept IV */
Packit 549fdc
				return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit 549fdc
			SR(cc->setiv(handle->handle, iv->data, iv->size),
Packit 549fdc
			   cc_cleanup);
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
		return 0;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
 fallback:
Packit 549fdc
	handle->encrypt = _gnutls_cipher_ops.encrypt;
Packit 549fdc
	handle->decrypt = _gnutls_cipher_ops.decrypt;
Packit 549fdc
	handle->aead_encrypt = _gnutls_cipher_ops.aead_encrypt;
Packit 549fdc
	handle->aead_decrypt = _gnutls_cipher_ops.aead_decrypt;
Packit 549fdc
	handle->deinit = _gnutls_cipher_ops.deinit;
Packit 549fdc
	handle->auth = _gnutls_cipher_ops.auth;
Packit 549fdc
	handle->tag = _gnutls_cipher_ops.tag;
Packit 549fdc
	handle->setiv = _gnutls_cipher_ops.setiv;
Packit 549fdc
Packit 549fdc
	/* otherwise use generic cipher interface
Packit 549fdc
	 */
Packit 549fdc
	ret = _gnutls_cipher_ops.init(e->id, &handle->handle, enc);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		return ret;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	ret =
Packit 549fdc
	    _gnutls_cipher_ops.setkey(handle->handle, key->data,
Packit 549fdc
				      key->size);
Packit 549fdc
	if (ret < 0) {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		goto cc_cleanup;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (iv) {
Packit 549fdc
		ret =
Packit 549fdc
		    _gnutls_cipher_ops.setiv(handle->handle, iv->data,
Packit 549fdc
					     iv->size);
Packit 549fdc
		if (ret < 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto cc_cleanup;
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
Packit 549fdc
      cc_cleanup:
Packit 549fdc
Packit 549fdc
	if (handle->handle)
Packit 549fdc
		handle->deinit(handle->handle);
Packit 549fdc
Packit 549fdc
	return ret;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Auth_cipher API 
Packit 549fdc
 */
Packit 549fdc
int _gnutls_auth_cipher_init(auth_cipher_hd_st * handle,
Packit 549fdc
			     const cipher_entry_st * e,
Packit 549fdc
			     const gnutls_datum_t * cipher_key,
Packit 549fdc
			     const gnutls_datum_t * iv,
Packit 549fdc
			     const mac_entry_st * me,
Packit 549fdc
			     const gnutls_datum_t * mac_key,
Packit 549fdc
			     unsigned etm,
Packit 549fdc
#ifdef ENABLE_SSL3
Packit 549fdc
			     unsigned ssl_hmac,
Packit 549fdc
#endif
Packit 549fdc
			     int enc)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	if (unlikely(e == NULL))
Packit 549fdc
		return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
Packit 549fdc
Packit 549fdc
	FAIL_IF_LIB_ERROR;
Packit 549fdc
Packit 549fdc
	memset(handle, 0, sizeof(*handle));
Packit 549fdc
	handle->etm = etm;
Packit 549fdc
Packit 549fdc
	if (e->id != GNUTLS_CIPHER_NULL) {
Packit 549fdc
		handle->non_null = 1;
Packit 549fdc
		ret =
Packit 549fdc
		    _gnutls_cipher_init(&handle->cipher, e, cipher_key, iv,
Packit 549fdc
					enc);
Packit 549fdc
		if (ret < 0)
Packit 549fdc
			return gnutls_assert_val(ret);
Packit 549fdc
	} else
Packit 549fdc
		handle->non_null = 0;
Packit 549fdc
Packit 549fdc
	if (me->id != GNUTLS_MAC_AEAD) {
Packit 549fdc
		handle->is_mac = 1;
Packit 549fdc
#ifdef ENABLE_SSL3
Packit 549fdc
		handle->ssl_hmac = ssl_hmac;
Packit 549fdc
Packit 549fdc
		if (ssl_hmac)
Packit 549fdc
			ret =
Packit 549fdc
			    _gnutls_mac_init_ssl3(&handle->mac.dig, me,
Packit 549fdc
						  mac_key->data,
Packit 549fdc
						  mac_key->size);
Packit 549fdc
		else
Packit 549fdc
#endif
Packit 549fdc
			ret =
Packit 549fdc
			    _gnutls_mac_init(&handle->mac.mac, me,
Packit 549fdc
					     mac_key->data, mac_key->size);
Packit 549fdc
		if (ret < 0) {
Packit 549fdc
			gnutls_assert();
Packit 549fdc
			goto cleanup;
Packit 549fdc
		}
Packit 549fdc
Packit 549fdc
		handle->tag_size = _gnutls_mac_get_algo_len(me);
Packit 549fdc
	} else if (_gnutls_cipher_algo_is_aead(e)) {
Packit 549fdc
		handle->tag_size = _gnutls_cipher_get_tag_size(e);
Packit 549fdc
	} else {
Packit 549fdc
		gnutls_assert();
Packit 549fdc
		ret = GNUTLS_E_INVALID_REQUEST;
Packit 549fdc
		goto cleanup;
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
      cleanup:
Packit 549fdc
	if (handle->non_null != 0)
Packit 549fdc
		_gnutls_cipher_deinit(&handle->cipher);
Packit 549fdc
	return ret;
Packit 549fdc
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
#ifdef ENABLE_SSL3
Packit 549fdc
# define MAC(handle, text, textlen) \
Packit 549fdc
		if (handle->ssl_hmac) { \
Packit 549fdc
			ret = \
Packit 549fdc
			    _gnutls_hash(&handle->mac.dig, text, textlen); \
Packit 549fdc
		} else { \
Packit 549fdc
			ret = _gnutls_mac(&handle->mac.mac, text, textlen); \
Packit 549fdc
		} \
Packit 549fdc
		if (unlikely(ret < 0)) \
Packit 549fdc
			return gnutls_assert_val(ret)
Packit 549fdc
#else
Packit 549fdc
# define MAC(handle, text, textlen) \
Packit 549fdc
		ret = _gnutls_mac(&handle->mac.mac, text, textlen); \
Packit 549fdc
		if (unlikely(ret < 0)) \
Packit 549fdc
			return gnutls_assert_val(ret)
Packit 549fdc
#endif
Packit 549fdc
Packit 549fdc
int _gnutls_auth_cipher_add_auth(auth_cipher_hd_st * handle,
Packit 549fdc
				 const void *text, int textlen)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	if (handle->is_mac) {
Packit 549fdc
		MAC(handle, text, textlen);
Packit 549fdc
	} else if (_gnutls_cipher_is_aead(&handle->cipher))
Packit 549fdc
		return _gnutls_cipher_auth(&handle->cipher, text, textlen);
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
Packit 549fdc
/* The caller must make sure that textlen+pad_size+tag_size is divided by the block size of the cipher */
Packit 549fdc
int _gnutls_auth_cipher_encrypt2_tag(auth_cipher_hd_st * handle,
Packit 549fdc
				     const uint8_t * text, int textlen,
Packit 549fdc
				     void *_ciphertext, int ciphertextlen,
Packit 549fdc
				     int pad_size)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
	uint8_t *ciphertext = _ciphertext;
Packit 549fdc
	unsigned blocksize =
Packit 549fdc
	    _gnutls_cipher_get_block_size(handle->cipher.e);
Packit 549fdc
	unsigned l;
Packit 549fdc
Packit 549fdc
	if (handle->is_mac) { /* cipher + mac */
Packit 549fdc
		if (handle->non_null == 0) { /* NULL cipher + MAC */
Packit 549fdc
			MAC(handle, text, textlen);
Packit 549fdc
Packit 549fdc
			if (unlikely(textlen + pad_size + handle->tag_size) >
Packit 549fdc
			    ciphertextlen)
Packit 549fdc
				return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
Packit 549fdc
Packit 549fdc
			if (text != ciphertext)
Packit 549fdc
				memcpy(ciphertext, text, textlen);
Packit 549fdc
			ret =
Packit 549fdc
			    _gnutls_auth_cipher_tag(handle,
Packit 549fdc
						    ciphertext + textlen,
Packit 549fdc
						    handle->tag_size);
Packit 549fdc
			if (ret < 0)
Packit 549fdc
				return gnutls_assert_val(ret);
Packit 549fdc
Packit 549fdc
		} else {
Packit 549fdc
			uint8_t *orig_ciphertext = ciphertext;
Packit 549fdc
Packit 549fdc
			if (handle->etm == 0 || handle->cipher.e->type != CIPHER_BLOCK) {
Packit 549fdc
				MAC(handle, text, textlen);
Packit 549fdc
			}
Packit 549fdc
Packit 549fdc
			if (unlikely(textlen + pad_size + handle->tag_size) >
Packit 549fdc
			    ciphertextlen)
Packit 549fdc
				return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
Packit 549fdc
Packit 549fdc
			assert(blocksize != 0);
Packit 549fdc
			l = (textlen / blocksize) * blocksize;
Packit 549fdc
			if (l > 0) {
Packit 549fdc
				ret =
Packit 549fdc
					_gnutls_cipher_encrypt2(&handle->cipher, text,
Packit 549fdc
							    l, ciphertext,
Packit 549fdc
							    ciphertextlen);
Packit 549fdc
				if (ret < 0)
Packit 549fdc
					return gnutls_assert_val(ret);
Packit 549fdc
Packit 549fdc
				textlen -= l;
Packit 549fdc
				text += l;
Packit 549fdc
				ciphertext += l;
Packit 549fdc
				ciphertextlen -= l;
Packit 549fdc
			}
Packit 549fdc
Packit 549fdc
			if (ciphertext != text && textlen > 0)
Packit 549fdc
				memcpy(ciphertext, text, textlen);
Packit 549fdc
Packit 549fdc
			if (handle->etm == 0 || handle->cipher.e->type != CIPHER_BLOCK) {
Packit 549fdc
				ret =
Packit 549fdc
				    _gnutls_auth_cipher_tag(handle,
Packit 549fdc
							    ciphertext + textlen,
Packit 549fdc
							    handle->tag_size);
Packit 549fdc
				if (ret < 0)
Packit 549fdc
					return gnutls_assert_val(ret);
Packit 549fdc
				textlen += handle->tag_size;
Packit 549fdc
			}
Packit 549fdc
Packit 549fdc
			/* TLS 1.0 style padding */
Packit 549fdc
			if (pad_size > 0) {
Packit 549fdc
				memset(ciphertext + textlen, pad_size - 1,
Packit 549fdc
				       pad_size);
Packit 549fdc
				textlen += pad_size;
Packit 549fdc
			}
Packit 549fdc
Packit 549fdc
			ret =
Packit 549fdc
			    _gnutls_cipher_encrypt2(&handle->cipher,
Packit 549fdc
						    ciphertext, textlen,
Packit 549fdc
						    ciphertext,
Packit 549fdc
						    ciphertextlen);
Packit 549fdc
			if (ret < 0)
Packit 549fdc
				return gnutls_assert_val(ret);
Packit 549fdc
Packit 549fdc
			if (handle->etm != 0 && handle->cipher.e->type == CIPHER_BLOCK) {
Packit 549fdc
				MAC(handle, orig_ciphertext, l);
Packit 549fdc
				MAC(handle, ciphertext, textlen);
Packit 549fdc
Packit 549fdc
				ret =
Packit 549fdc
					_gnutls_auth_cipher_tag(handle,
Packit 549fdc
							ciphertext + textlen,
Packit 549fdc
							handle->tag_size);
Packit 549fdc
				if (ret < 0)
Packit 549fdc
					return gnutls_assert_val(ret);
Packit 549fdc
			}
Packit 549fdc
		}
Packit 549fdc
	} else if (_gnutls_cipher_is_aead(&handle->cipher)) {
Packit 549fdc
		ret =
Packit 549fdc
		    _gnutls_cipher_encrypt2(&handle->cipher, text, textlen,
Packit 549fdc
					    ciphertext, ciphertextlen);
Packit 549fdc
		if (unlikely(ret < 0))
Packit 549fdc
			return gnutls_assert_val(ret);
Packit 549fdc
Packit 549fdc
		ret =
Packit 549fdc
		    _gnutls_auth_cipher_tag(handle, ciphertext + textlen,
Packit 549fdc
					    handle->tag_size);
Packit 549fdc
		if (unlikely(ret < 0))
Packit 549fdc
			return gnutls_assert_val(ret);
Packit 549fdc
	} else if (handle->non_null == 0 && text != ciphertext) /* NULL cipher - no MAC */
Packit 549fdc
		memcpy(ciphertext, text, textlen);
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
int _gnutls_auth_cipher_decrypt2(auth_cipher_hd_st * handle,
Packit 549fdc
				 const void *ciphertext, int ciphertextlen,
Packit 549fdc
				 void *text, int textlen)
Packit 549fdc
{
Packit 549fdc
	int ret;
Packit 549fdc
Packit 549fdc
	if (unlikely(ciphertextlen > textlen))
Packit 549fdc
		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
Packit 549fdc
Packit 549fdc
	if (handle->is_mac && (handle->etm != 0 && handle->cipher.e->type == CIPHER_BLOCK)) {
Packit 549fdc
		/* The MAC is not to be hashed */
Packit 549fdc
		ciphertextlen -= handle->tag_size;
Packit 549fdc
Packit 549fdc
		MAC(handle, ciphertext, ciphertextlen);
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	if (handle->non_null != 0) {
Packit 549fdc
		ret =
Packit 549fdc
		    _gnutls_cipher_decrypt2(&handle->cipher, ciphertext,
Packit 549fdc
					    ciphertextlen, text, textlen);
Packit 549fdc
		if (ret < 0)
Packit 549fdc
			return gnutls_assert_val(ret);
Packit 549fdc
	} else if (handle->non_null == 0 && text != ciphertext)
Packit 549fdc
		memcpy(text, ciphertext, ciphertextlen);
Packit 549fdc
Packit 549fdc
	if (handle->is_mac && (handle->etm == 0 || handle->cipher.e->type != CIPHER_BLOCK)) {
Packit 549fdc
		/* The MAC is not to be hashed */
Packit 549fdc
		ciphertextlen -= handle->tag_size;
Packit 549fdc
Packit 549fdc
		MAC(handle, text, ciphertextlen);
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
int _gnutls_auth_cipher_tag(auth_cipher_hd_st * handle, void *tag,
Packit 549fdc
			    int tag_size)
Packit 549fdc
{
Packit 549fdc
	if (handle->is_mac) {
Packit 549fdc
#ifdef ENABLE_SSL3
Packit 549fdc
		int ret;
Packit 549fdc
Packit 549fdc
		if (handle->ssl_hmac) {
Packit 549fdc
			ret =
Packit 549fdc
			    _gnutls_mac_output_ssl3(&handle->mac.dig, tag);
Packit 549fdc
			if (ret < 0)
Packit 549fdc
				return gnutls_assert_val(ret);
Packit 549fdc
		} else
Packit 549fdc
#endif
Packit 549fdc
			_gnutls_mac_output(&handle->mac.mac, tag);
Packit 549fdc
	} else if (_gnutls_cipher_is_aead(&handle->cipher)) {
Packit 549fdc
		_gnutls_cipher_tag(&handle->cipher, tag, tag_size);
Packit 549fdc
	} else
Packit 549fdc
		memset(tag, 0, tag_size);
Packit 549fdc
Packit 549fdc
	return 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
void _gnutls_auth_cipher_deinit(auth_cipher_hd_st * handle)
Packit 549fdc
{
Packit 549fdc
	if (handle->is_mac) {
Packit 549fdc
#ifdef ENABLE_SSL3
Packit 549fdc
		if (handle->ssl_hmac)	/* failure here doesn't matter */
Packit 549fdc
			_gnutls_mac_deinit_ssl3(&handle->mac.dig, NULL);
Packit 549fdc
		else
Packit 549fdc
#endif
Packit 549fdc
			_gnutls_mac_deinit(&handle->mac.mac, NULL);
Packit 549fdc
	}
Packit 549fdc
	if (handle->non_null != 0)
Packit 549fdc
		_gnutls_cipher_deinit(&handle->cipher);
Packit 549fdc
}